#!/usr/bin/env php 
<?php

$web = 'exakat';

if (in_array('phar', stream_get_wrappers()) && class_exists('Phar', 0)) {
Phar::interceptFileFuncs();
set_include_path('phar://' . __FILE__ . PATH_SEPARATOR . get_include_path());
Phar::webPhar(null, $web);
include 'phar://' . __FILE__ . '/' . Extract_Phar::START;
return;
}

if (@(isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'POST'))) {
Extract_Phar::go(true);
$mimes = array(
'phps' => 2,
'c' => 'text/plain',
'cc' => 'text/plain',
'cpp' => 'text/plain',
'c++' => 'text/plain',
'dtd' => 'text/plain',
'h' => 'text/plain',
'log' => 'text/plain',
'rng' => 'text/plain',
'txt' => 'text/plain',
'xsd' => 'text/plain',
'php' => 1,
'inc' => 1,
'avi' => 'video/avi',
'bmp' => 'image/bmp',
'css' => 'text/css',
'gif' => 'image/gif',
'htm' => 'text/html',
'html' => 'text/html',
'htmls' => 'text/html',
'ico' => 'image/x-ico',
'jpe' => 'image/jpeg',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'js' => 'application/x-javascript',
'midi' => 'audio/midi',
'mid' => 'audio/midi',
'mod' => 'audio/mod',
'mov' => 'movie/quicktime',
'mp3' => 'audio/mp3',
'mpg' => 'video/mpeg',
'mpeg' => 'video/mpeg',
'pdf' => 'application/pdf',
'png' => 'image/png',
'swf' => 'application/shockwave-flash',
'tif' => 'image/tiff',
'tiff' => 'image/tiff',
'wav' => 'audio/wav',
'xbm' => 'image/xbm',
'xml' => 'text/xml',
);

header("Cache-Control: no-cache, must-revalidate");
header("Pragma: no-cache");

$basename = basename(__FILE__);
if (!strpos($_SERVER['REQUEST_URI'], $basename)) {
chdir(Extract_Phar::$temp);
include $web;
return;
}
$pt = substr($_SERVER['REQUEST_URI'], strpos($_SERVER['REQUEST_URI'], $basename) + strlen($basename));
if (!$pt || $pt == '/') {
$pt = $web;
header('HTTP/1.1 301 Moved Permanently');
header('Location: ' . $_SERVER['REQUEST_URI'] . '/' . $pt);
exit;
}
$a = realpath(Extract_Phar::$temp . DIRECTORY_SEPARATOR . $pt);
if (!$a || strlen(dirname($a)) < strlen(Extract_Phar::$temp)) {
header('HTTP/1.0 404 Not Found');
echo "<html>\n <head>\n  <title>File Not Found<title>\n </head>\n <body>\n  <h1>404 - File Not Found</h1>\n </body>\n</html>";
exit;
}
$b = pathinfo($a);
if (!isset($b['extension'])) {
header('Content-Type: text/plain');
header('Content-Length: ' . filesize($a));
readfile($a);
exit;
}
if (isset($mimes[$b['extension']])) {
if ($mimes[$b['extension']] === 1) {
include $a;
exit;
}
if ($mimes[$b['extension']] === 2) {
highlight_file($a);
exit;
}
header('Content-Type: ' .$mimes[$b['extension']]);
header('Content-Length: ' . filesize($a));
readfile($a);
exit;
}
}

class Extract_Phar
{
static $temp;
static $origdir;
const GZ = 0x1000;
const BZ2 = 0x2000;
const MASK = 0x3000;
const START = 'exakat';
const LEN = 6637;

static function go($return = false)
{
$fp = fopen(__FILE__, 'rb');
fseek($fp, self::LEN);
$L = unpack('V', $a = fread($fp, 4));
$m = '';

do {
$read = 8192;
if ($L[1] - strlen($m) < 8192) {
$read = $L[1] - strlen($m);
}
$last = fread($fp, $read);
$m .= $last;
} while (strlen($last) && strlen($m) < $L[1]);

if (strlen($m) < $L[1]) {
die('ERROR: manifest length read was "' .
strlen($m) .'" should be "' .
$L[1] . '"');
}

$info = self::_unpack($m);
$f = $info['c'];

if ($f & self::GZ) {
if (!function_exists('gzinflate')) {
die('Error: zlib extension is not enabled -' .
' gzinflate() function needed for zlib-compressed .phars');
}
}

if ($f & self::BZ2) {
if (!function_exists('bzdecompress')) {
die('Error: bzip2 extension is not enabled -' .
' bzdecompress() function needed for bz2-compressed .phars');
}
}

$temp = self::tmpdir();

if (!$temp || !is_writable($temp)) {
$sessionpath = session_save_path();
if (strpos ($sessionpath, ";") !== false)
$sessionpath = substr ($sessionpath, strpos ($sessionpath, ";")+1);
if (!file_exists($sessionpath) || !is_dir($sessionpath)) {
die('Could not locate temporary directory to extract phar');
}
$temp = $sessionpath;
}

$temp .= '/pharextract/'.basename(__FILE__, '.phar');
self::$temp = $temp;
self::$origdir = getcwd();
@mkdir($temp, 0777, true);
$temp = realpath($temp);

if (!file_exists($temp . DIRECTORY_SEPARATOR . md5_file(__FILE__))) {
self::_removeTmpFiles($temp, getcwd());
@mkdir($temp, 0777, true);
@file_put_contents($temp . '/' . md5_file(__FILE__), '');

foreach ($info['m'] as $path => $file) {
$a = !file_exists(dirname($temp . '/' . $path));
@mkdir(dirname($temp . '/' . $path), 0777, true);
clearstatcache();

if ($path[strlen($path) - 1] == '/') {
@mkdir($temp . '/' . $path, 0777);
} else {
file_put_contents($temp . '/' . $path, self::extractFile($path, $file, $fp));
@chmod($temp . '/' . $path, 0666);
}
}
}

chdir($temp);

if (!$return) {
include self::START;
}
}

static function tmpdir()
{
if (strpos(PHP_OS, 'WIN') !== false) {
if ($var = getenv('TMP') ? getenv('TMP') : getenv('TEMP')) {
return $var;
}
if (is_dir('/temp') || mkdir('/temp')) {
return realpath('/temp');
}
return false;
}
if ($var = getenv('TMPDIR')) {
return $var;
}
return realpath('/tmp');
}

static function _unpack($m)
{
$info = unpack('V', substr($m, 0, 4));
 $l = unpack('V', substr($m, 10, 4));
$m = substr($m, 14 + $l[1]);
$s = unpack('V', substr($m, 0, 4));
$o = 0;
$start = 4 + $s[1];
$ret['c'] = 0;

for ($i = 0; $i < $info[1]; $i++) {
 $len = unpack('V', substr($m, $start, 4));
$start += 4;
 $savepath = substr($m, $start, $len[1]);
$start += $len[1];
   $ret['m'][$savepath] = array_values(unpack('Va/Vb/Vc/Vd/Ve/Vf', substr($m, $start, 24)));
$ret['m'][$savepath][3] = sprintf('%u', $ret['m'][$savepath][3]
& 0xffffffff);
$ret['m'][$savepath][7] = $o;
$o += $ret['m'][$savepath][2];
$start += 24 + $ret['m'][$savepath][5];
$ret['c'] |= $ret['m'][$savepath][4] & self::MASK;
}
return $ret;
}

static function extractFile($path, $entry, $fp)
{
$data = '';
$c = $entry[2];

while ($c) {
if ($c < 8192) {
$data .= @fread($fp, $c);
$c = 0;
} else {
$c -= 8192;
$data .= @fread($fp, 8192);
}
}

if ($entry[4] & self::GZ) {
$data = gzinflate($data);
} elseif ($entry[4] & self::BZ2) {
$data = bzdecompress($data);
}

if (strlen($data) != $entry[0]) {
die("Invalid internal .phar file (size error " . strlen($data) . " != " .
$stat[7] . ")");
}

if ($entry[3] != sprintf("%u", crc32($data) & 0xffffffff)) {
die("Invalid internal .phar file (checksum error)");
}

return $data;
}

static function _removeTmpFiles($temp, $origdir)
{
chdir($temp);

foreach (glob('*') as $f) {
if (file_exists($f)) {
is_dir($f) ? @rmdir($f) : @unlink($f);
if (file_exists($f) && is_dir($f)) {
self::_removeTmpFiles($f, getcwd());
}
}
}

@rmdir($temp);
clearstatcache();
chdir($origdir);
}
}

Extract_Phar::go();
__HALT_COMPILER(); ?>
* M         exakat.ce.phar    7   library/Exakat/Analyzer/Complete/PropagateConstants.php\  rb\  !j$      *   library/Exakat/Analyzer/Classes/NonPpp.php  rb  ?i*      8   library/Exakat/Analyzer/Complete/SetParentDefinition.php  rb  7X      :   library/Exakat/Analyzer/Complete/OverwrittenProperties.phpw	  rbw	  :      A   library/Exakat/Analyzer/Classes/StaticMethodsCalledFromObject.php'  rb'  #      3   library/Exakat/Analyzer/Constants/Constantnames.php  rb  @      ;   library/Exakat/Analyzer/Functions/RedeclaredPhpFunction.php&  rb&  2q      @   library/Exakat/Analyzer/Structures/ErrorReportingWithInteger.php  rb        5   library/Exakat/Analyzer/Structures/NoDirectAccess.php  rb        -   library/Exakat/Analyzer/Files/IsCliScript.php7  rb7  (      :   library/Exakat/Analyzer/Structures/ForgottenWhiteSpace.php  rb  1'#      /   library/Exakat/Analyzer/Structures/Noscream.php#  rb#  g      -   library/Exakat/Analyzer/Structures/NotNot.php  rb  8\      4   library/Exakat/Analyzer/Structures/StrposCompare.phps  rbs  pa      6   library/Exakat/Analyzer/Structures/ThrowsAndAssign.php  rb  )      3   library/Exakat/Analyzer/Structures/VardumpUsage.php3	  rb3	  8H>      I   library/Exakat/Analyzer/Complete/SetClassRemoteDefinitionWithTypehint.php^)  rb^)  	4Z      @   library/Exakat/Analyzer/Constants/MultipleConstantDefinition.php  rb  G,      <   library/Exakat/Analyzer/Functions/WrongOptionalParameter.php  rb  ,      1   library/Exakat/Analyzer/Php/IsnullVsEqualNull.phpD  rbD  l      3   library/Exakat/Analyzer/Type/OneVariableStrings.php  rb  }P      6   library/Exakat/Analyzer/Classes/StaticContainsThis.php  rb  
      4   library/Exakat/Analyzer/Structures/WhileListEach.php  rb  M      :   library/Exakat/Analyzer/Structures/MultipleDefinedCase.php  rb        ;   library/Exakat/Analyzer/Structures/SwitchWithoutDefault.phpR  rbR  ;Ƥ      4   library/Exakat/Analyzer/Structures/NestedTernary.php  rb  #E      >   library/Exakat/Analyzer/Complete/SetStringMethodDefinition.php  rb  A      7   library/Exakat/Analyzer/Structures/Htmlentitiescall.php&  rb&  `      9   library/Exakat/Analyzer/Complete/OverwrittenConstants.php  rb  }]\      .   library/Exakat/Analyzer/Classes/ClassUsage.php  rb  >ٓ~      5   library/Exakat/Analyzer/Composer/IsComposerNsname.php%  rb%  0      3   library/Exakat/Analyzer/Functions/IsExtFunction.phpn  rbn        5   library/Exakat/Analyzer/Interfaces/InterfaceUsage.phpp  rbp  -/      -   library/Exakat/Analyzer/Traits/TraitUsage.php  rb  p      5   library/Exakat/Analyzer/Namespaces/NamespaceUsage.phpA  rbA  .%Y      /   library/Exakat/Analyzer/Php/DirectivesUsage.php|  rb|  Z      >   library/Exakat/Analyzer/Structures/DanglingArrayReferences.phpL  rbL  ^:      2   library/Exakat/Analyzer/Functions/AliasesUsage.php  rb  p_2      :   library/Exakat/Analyzer/Functions/UsesDefaultArguments.php  rb  feҤ      7   library/Exakat/Analyzer/Complete/OverwrittenMethods.php  rb  pPؤ      7   library/Exakat/Analyzer/Functions/VariableArguments.php+  rb+  ?       ;   library/Exakat/Analyzer/Complete/CreateCompactVariables.php  rb  X      -   library/Exakat/Analyzer/Functions/CantUse.php  rb  X?L      9   library/Exakat/Analyzer/Exceptions/OverwriteException.php  rb  E      4   library/Exakat/Analyzer/Classes/HasMagicProperty.php  rb  Us;s      >   library/Exakat/Analyzer/Structures/BooleanStrictComparison.php  rb  դ      0   library/Exakat/Analyzer/Structures/LoneBlock.php  rb        0   library/Exakat/Analyzer/Php/LogicalInLetters.php  rb  M      1   library/Exakat/Analyzer/Classes/ConstantClass.php  rb  +      4   library/Exakat/Analyzer/Structures/RepeatedPrint.php6  rb6  5HI      >   library/Exakat/Analyzer/Structures/PrintWithoutParenthesis.php  rb  y:      7   library/Exakat/Analyzer/Structures/ObjectReferences.phpa  rba  )      1   library/Exakat/Analyzer/Type/NoRealComparison.php  rb  me[      ;   library/Exakat/Analyzer/Classes/DirectCallToMagicMethod.phpz  rbz  0Ť      0   library/Exakat/Analyzer/Classes/UselessFinal.php  rb        2   library/Exakat/Analyzer/Structures/UseConstant.php  rb  a      3   library/Exakat/Analyzer/Structures/UselessUnset.php  rb  (      :   library/Exakat/Analyzer/Performances/ArrayMergeInLoops.php	  rb	  ppO      9   library/Exakat/Analyzer/Structures/UselessParenthesis.phpJ  rbJ  )      ,   library/Exakat/Analyzer/Php/UseObjectApi.php  rb  F|Ϥ      F   library/Exakat/Analyzer/Structures/AlteringForeachWithoutReference.php  rb         +   library/Exakat/Analyzer/Php/UsePathinfo.phpz  rbz  X      H   library/Exakat/Analyzer/Structures/NoParenthesisForLanguageConstruct.phpI  rbI  }C      3   library/Exakat/Analyzer/Constants/IsPhpConstant.phpx  rbx  <J<Ѥ      0   library/Exakat/Analyzer/Structures/ImpliedIf.php  rb  N`eD      ;   library/Exakat/Analyzer/Structures/ShouldChainException.php-  rb-  QI+      5   library/Exakat/Analyzer/Interfaces/IsExtInterface.php	  rb	  q6¤      4   library/Exakat/Analyzer/Composer/IsComposerClass.php  rb  ޙ%f      ?   library/Exakat/Analyzer/Security/ShouldUsePreparedStatement.php  rb  ව      2   library/Exakat/Analyzer/Structures/PrintAndDie.php  rb  Ж      9   library/Exakat/Analyzer/Structures/UncheckedResources.php  rb  ~      3   library/Exakat/Analyzer/Structures/ElseIfElseif.php  rb  ?~      8   library/Exakat/Analyzer/Classes/MultipleDeclarations.php~  rb~  ьB.      5   library/Exakat/Analyzer/Namespaces/EmptyNamespace.php  rb  fgt      ?   library/Exakat/Analyzer/Structures/CouldUseShortAssignation.phpz  rbz  %      9   library/Exakat/Analyzer/Performances/PrePostIncrement.php  rb  BΧ&      <   library/Exakat/Analyzer/Structures/IndicesAreIntOrString.php
  rb
   <]      /   library/Exakat/Analyzer/Type/ShouldTypecast.php  rb  22      2   library/Exakat/Analyzer/Structures/NoSubstrOne.php  rb  e!¤      6   library/Exakat/Analyzer/Structures/UselessBrackets.php  rb  )      2   library/Exakat/Analyzer/Structures/pregOptionE.phpD  rbD  s      5   library/Exakat/Analyzer/Structures/EvalWithoutTry.phpn  rbn  i$      4   library/Exakat/Analyzer/Structures/UseInstanceof.php  rb  >&      4   library/Exakat/Analyzer/Type/SilentlyCastInteger.phpV  rbV  h>w      :   library/Exakat/Analyzer/Structures/TimestampDifference.php}	  rb}	  dj      6   library/Exakat/Analyzer/Classes/RedefinedConstants.php4  rb4  ?S偤      4   library/Exakat/Analyzer/Classes/RedefinedDefault.php[	  rb[	  9      )   library/Exakat/Analyzer/Php/FopenMode.php  rb  6̤      2   library/Exakat/Analyzer/Structures/NegativePow.php  rb  r      *   library/Exakat/Analyzer/Php/BetterRand.phpi  rbi  F[i      6   library/Exakat/Analyzer/Structures/TernaryInConcat.phpN  rbN  t      :   library/Exakat/Analyzer/Structures/IdenticalConditions.php<  rb<  ůO      /   library/Exakat/Analyzer/Structures/NoChoice.phpQ  rbQ        6   library/Exakat/Analyzer/Structures/LogicalMistakes.phpo  rbo  @      5   library/Exakat/Analyzer/Structures/SameConditions.php  rb  竤      6   library/Exakat/Analyzer/Structures/ReturnTrueFalse.php2  rb2  !      2   library/Exakat/Analyzer/Structures/CouldUseDir.php  rb  ?X      1   library/Exakat/Analyzer/Php/ShouldUseCoalesce.php5  rb5  M       ;   library/Exakat/Analyzer/Structures/IfWithSameConditions.php  rb  +-      8   library/Exakat/Analyzer/Exceptions/ThrowFunctioncall.php  rb  $z      1   library/Exakat/Analyzer/Classes/UseInstanceof.php  rb  #      9   library/Exakat/Analyzer/Structures/ResultMayBeMissing.php  rb  o3Ԥ      4   library/Exakat/Analyzer/Structures/NeverNegative.phpp  rbp  Y      2   library/Exakat/Analyzer/Structures/EmptyBlocks.php  rb  p;{      3   library/Exakat/Analyzer/Classes/ThrowInDestruct.php  rb  ^K8      3   library/Exakat/Analyzer/Structures/UseSystemTmp.php-
  rb-
  v      0   library/Exakat/Analyzer/Namespaces/HiddenUse.php  rb  {      6   library/Exakat/Analyzer/Namespaces/ShouldMakeAlias.php2  rb2  O      <   library/Exakat/Analyzer/Classes/MultipleTraitOrInterface.php!	  rb!	  IG      ?   library/Exakat/Analyzer/Namespaces/MultipleAliasDefinitions.php  rb  ﮤ      >   library/Exakat/Analyzer/Structures/FailingSubstrComparison.phpH  rbH  G#      8   library/Exakat/Analyzer/Structures/ShouldMakeTernary.phpQ  rbQ  ;mi5      :   library/Exakat/Analyzer/Structures/DropElseAfterReturn.php  rb  =      4   library/Exakat/Analyzer/Classes/UseClassOperator.php  rb  K      2   library/Exakat/Analyzer/Security/DontEchoError.php  rb  S      7   library/Exakat/Analyzer/Structures/NoIssetWithEmpty.php  rb        3   library/Exakat/Analyzer/Structures/UselessCheck.php  rb  *͜      E   library/Exakat/Analyzer/Namespaces/MultipleAliasDefinitionPerFile.phpU  rbU  r      3   library/Exakat/Analyzer/Structures/DirThenSlash.php7  rb7  Ƌ      4   library/Exakat/Analyzer/Structures/RepeatedRegex.php]  rb]  E      /   library/Exakat/Analyzer/Php/NoClassInGlobal.php  rb  fg      8   library/Exakat/Analyzer/Structures/CouldUseStrrepeat.php  rb  Z      7   library/Exakat/Analyzer/Type/StringWithStrangeSpace.phpW  rbW  Ɩ      3   library/Exakat/Analyzer/Structures/NoEmptyRegex.php
  rb
  HKh      8   library/Exakat/Analyzer/Structures/NoReferenceOnLeft.php  rb  =o      G   library/Exakat/Analyzer/Complete/SetClassRemoteDefinitionWithGlobal.phpv  rbv  緝9      J   library/Exakat/Analyzer/Complete/SetClassRemoteDefinitionWithInjection.php  rb  N
9      L   library/Exakat/Analyzer/Complete/SetClassRemoteDefinitionWithParenthesis.php  rb  P_      -   library/Exakat/Analyzer/Structures/IsZero.phpQ  rbQ        ;   library/Exakat/Analyzer/Structures/UnconditionLoopBreak.php  rb  SF      4   library/Exakat/Analyzer/Structures/NextMonthTrap.php	  rb	  Z      7   library/Exakat/Analyzer/Structures/AutoUnsetForeach.php  rb  s      ;   library/Exakat/Analyzer/Structures/IdenticalOnBothSides.php  rb  oa^      5   library/Exakat/Analyzer/Php/NoReferenceForTernary.php  rb  랤      =   library/Exakat/Analyzer/Functions/UnusedInheritedVariable.php  rb        3   library/Exakat/Analyzer/Exceptions/UselessCatch.phpi  rbi  \M      7   library/Exakat/Analyzer/Classes/DontUnsetProperties.php  rb  !Qv      .   library/Exakat/Analyzer/Php/StrtrArguments.php  rb  vZH      9   library/Exakat/Analyzer/Structures/MissingParenthesis.php)  rb)  U)      6   library/Exakat/Analyzer/Performances/StrposTooMuch.php  rb  DI      :   library/Exakat/Analyzer/Functions/TypehintedReferences.php  rb  '      0   library/Exakat/Analyzer/Structures/CheckJson.php  rb  {* a      7   library/Exakat/Analyzer/Variables/UndefinedVariable.php]  rb]        8   library/Exakat/Analyzer/Functions/ShouldYieldWithKey.php  rb  W	      /   library/Exakat/Analyzer/Traits/UselessAlias.php  rb        1   library/Exakat/Analyzer/Php/MissingSubpattern.php  rb   b      8   library/Exakat/Analyzer/Structures/AssigneAndCompare.php  rb  yD      <   library/Exakat/Analyzer/Functions/TypehintMustBeReturned.php  rb  |      /   library/Exakat/Analyzer/Classes/IsNotFamily.php  rb  qH      5   library/Exakat/Analyzer/Structures/CastingTernary.php  rb  G¤      1   library/Exakat/Analyzer/Php/ConcatAndAddition.php  rb  5      1   library/Exakat/Analyzer/Functions/IsGenerator.php  rb  5      ,   library/Exakat/Analyzer/Classes/NoParent.php  rb  "u      ;   library/Exakat/Analyzer/Structures/ShouldUseExplodeArgs.php
  rb
  IQ      6   library/Exakat/Analyzer/Performances/UseArraySlice.phpx  rbx  u8      8   library/Exakat/Analyzer/Structures/CoalesceAndConcat.php  rb  /\ˢ      7   library/Exakat/Analyzer/Interfaces/IsNotImplemented.phpH  rbH  _      ?   library/Exakat/Analyzer/Interfaces/CantImplementTraversable.php  rb          2   library/Exakat/Analyzer/Structures/MergeIfThen.php  rb   k       /   library/Exakat/Analyzer/Structures/NotEqual.php3  rb3  FG      :   library/Exakat/Analyzer/Php/WrongTypeForNativeFunction.php  rb  ͯ""      .   library/Exakat/Analyzer/Structures/AddZero.phpH  rbH  L~      8   library/Exakat/Analyzer/Arrays/MultipleIdenticalKeys.php
  rb
        C   library/Exakat/Analyzer/Complete/SetClassMethodRemoteDefinition.php  rb        8   library/Exakat/Analyzer/Complete/CreateDefaultValues.php  rb  Ƥ      :   library/Exakat/Analyzer/Constants/ConstantStrangeNames.phpP  rbP  R      0   library/Exakat/Analyzer/Structures/ExitUsage.php  rb  b0ؤ      4   library/Exakat/Analyzer/Structures/MultiplyByOne.php  rb  1	[      8   library/Exakat/Analyzer/Complete/CreateMagicProperty.phps$  rbs$  5       3   library/Exakat/Analyzer/Constants/ConstantUsage.php
	  rb
	  XQi      @   library/Exakat/Analyzer/Complete/MakeClassConstantDefinition.php  rb  iTK      .   library/Exakat/Analyzer/Classes/IsExtClass.phph  rbh  h.=      8   library/Exakat/Analyzer/Functions/UndefinedFunctions.phpA  rbA  f      ?   library/Exakat/Analyzer/Variables/VariableUsedOnceByContext.php  rb  UU	      ,   library/Exakat/Analyzer/Structures/OrDie.phpT  rbT  ؤ      0   library/Exakat/Analyzer/Functions/MustReturn.phpC  rbC  C       3   library/Exakat/Analyzer/Classes/DefinedProperty.phpX	  rbX	        <   library/Exakat/Analyzer/Functions/UseConstantAsArguments.php   rb   
h      8   library/Exakat/Analyzer/Composer/IsComposerInterface.php  rb  ZӤ      5   library/Exakat/Analyzer/Php/InternalParameterType.php  rb  ZT#      1   library/Exakat/Analyzer/Traits/UndefinedTrait.php2  rb2  ~5      6   library/Exakat/Analyzer/Structures/PrintfArguments.php5  rb5  1ؤ      3   library/Exakat/Analyzer/Structures/InvalidRegex.phpb  rbb  gZ@      <   library/Exakat/Analyzer/Classes/UndeclaredStaticProperty.php
  rb
  <      4   library/Exakat/Analyzer/Classes/CheckOnCallUsage.php
  rb
  	      >   library/Exakat/Analyzer/Structures/StripTagsSkipsClosedTag.php  rb  E      3   library/Exakat/Analyzer/Complete/PropagateCalls.phpc  rbc  	9      4   library/Exakat/Analyzer/Classes/IsaMagicProperty.php  rb   ;      3   library/Exakat/Analyzer/Constants/IsExtConstant.php)  rb)  QP      9   library/Exakat/Analyzer/Constants/CustomConstantUsage.php  rb  Q      4   library/Exakat/Analyzer/Classes/DefinedConstants.php  rb  j1Τ      >   library/Exakat/Analyzer/Complete/MakeClassMethodDefinition.phpxV  rbxV  V      6   library/Exakat/Analyzer/Constants/ConstRecommended.php;  rb;  /f      5   library/Exakat/Analyzer/Classes/UndefinedProperty.php  rb        :   library/Exakat/Analyzer/Interfaces/UndefinedInterfaces.php
  rb
  sd      I   library/Exakat/Analyzer/Complete/SetClassRemoteDefinitionWithLocalNew.php  rb  (p      O   library/Exakat/Analyzer/Complete/SetClassRemoteDefinitionWithReturnTypehint.php  rb  C+      8   library/Exakat/Analyzer/Classes/UndefinedStaticclass.php<  rb<  iF      %   library/Exakat/Analyzer/Type/Pack.phpZ  rbZ  Eb      7   library/Exakat/Analyzer/Structures/ImplodeArgsOrder.phpr  rbr  
76      ;   library/Exakat/Analyzer/Functions/NoLiteralForReference.php  rb  $@      <   library/Exakat/Analyzer/Complete/FollowClosureDefinition.php  rb  nz{      1   library/Exakat/Analyzer/Typehints/CouldBeNull.php`  rb`  	gP      3   library/Exakat/Analyzer/Typehints/CouldBeString.php  rb  kZ      2   library/Exakat/Analyzer/Typehints/CouldBeFloat.phpR  rbR  b      0   library/Exakat/Analyzer/Typehints/CouldBeInt.php=  rb=  D      4   library/Exakat/Analyzer/Typehints/CouldBeBoolean.php  rb  n^      2   library/Exakat/Analyzer/Typehints/CouldBeArray.phpc  rbc  :      0   library/Exakat/Analyzer/Typehints/CouldBeCIT.phpW  rbW        4   library/Exakat/Analyzer/Structures/ListOmissions.phpK  rbK  ~      4   library/Exakat/Analyzer/Classes/NoMagicWithArray.php  rb  {AT      5   library/Exakat/Analyzer/Traits/UndefinedInsteadof.php(  rb(  ^fn      <   library/Exakat/Analyzer/Complete/SetArrayClassDefinition.php  rb  l       9   library/Exakat/Analyzer/Structures/UselessInstruction.php@+  rb@+  Y      8   library/Exakat/Analyzer/Constants/UndefinedConstants.phpr	  rbr	  <      6   library/Exakat/Analyzer/Classes/UndefinedConstants.php   rb   >j      *   library/Exakat/Analyzer/Php/Deprecated.php(  rb(  
      <   library/Exakat/Analyzer/Functions/WrongNumberOfArguments.phppA  rbpA  8C2      B   library/Exakat/Analyzer/Complete/MakeFunctioncallWithReference.php	  rb	  ?m      5   library/Exakat/Analyzer/Structures/UselessCasting.php  rb  6
      .   library/Exakat/Analyzer/Functions/KillsApp.php"  rb"  	      8   library/Exakat/Analyzer/Structures/InvalidPackFormat.php  rb  s@!      7   library/Exakat/Analyzer/Functions/WrongReturnedType.php  rb         2   library/Exakat/Analyzer/Php/ScalarAreNotArrays.php  rb  sW      -   library/Exakat/Analyzer/Php/IsAWithString.php  rb  `      >   library/Exakat/Analyzer/Structures/MbstringUnknownEncoding.php  rb  z      7   library/Exakat/Analyzer/Structures/MbstringThirdArg.php0  rb0  zJb      7   library/Exakat/Analyzer/Functions/WrongTypeWithCall.phpk  rbk  >:      :   library/Exakat/Analyzer/Classes/WrongTypedPropertyInit.php  rb  }N      :   library/Exakat/Analyzer/Functions/UnknownParameterName.php  rb  '      7   library/Exakat/Analyzer/Typehints/MissingReturntype.phpp
  rbp
  xޤ      @   library/Exakat/Analyzer/Classes/NonStaticMethodsCalledStatic.php1  rb1   U{      D   library/Exakat/Analyzer/Structures/ForeachReferenceIsNotModified.phpx  rbx  {!h      )   library/Exakat/Analyzer/Php/AssignAnd.php  rb  ʳߤ      9   library/Exakat/Analyzer/Functions/CallbackNeedsReturn.php  rb  A      1   library/Exakat/Analyzer/Functions/DynamicCode.php{  rb{  LWЧ      3   library/Exakat/Analyzer/Variables/SelfTransform.php  rb  5#      +   library/Exakat/Analyzer/Variables/Blind.php  rb  U"#      3   library/Exakat/Analyzer/Classes/ExtendsStdclass.php  rb           library/Exakat/Reports/None.php  rb  \{      "   library/Exakat/Reports/Perfile.php
  rb
  7	         library/Exakat/Reports/Text.php.  rb.  ڠ          library/Exakat/Reports/Sarif.php  rb         "   library/Exakat/Reports/Reports.php/  rb/  &      )   library/Exakat/Reports/Phpcompilation.php  rb  =:      #   library/Exakat/Reports/Diplomat.php  rb        #   library/Exakat/Reports/Emissary.php̗ rb̗ o9ta      $   library/Exakat/Autoload/Autoload.php2  rb2  $      '   library/Exakat/Autoload/AutoloadDev.php:  rb:        '   library/Exakat/Autoload/AutoloadExt.php  rb  >|      &   library/Exakat/Autoload/Autoloader.php  rb  { e      6   library/Exakat/Analyzer/Complete/CreateMagicMethod.php  rb  i?      6   library/Exakat/Analyzer/Complete/SolveTraitMethods.php  rb  9      5   library/Exakat/Analyzer/Complete/VariableTypehint.phpc"  rbc"  YN      9   library/Exakat/Analyzer/Complete/CreateForeachDefault.php%  rb%  ?V{      -   library/Exakat/Analyzer/Complete/Complete.php  rb  Ԥ      *   library/Exakat/Analyzer/Complete/.DS_Store  rb  Q֤      3   library/Exakat/Analyzer/Complete/IsPhpStructure.php  rb  K0&u      =   library/Exakat/Analyzer/Complete/PhpExtStubPropertyMethod.php  rb        4   library/Exakat/Analyzer/Complete/IsStubStructure.php,$  rb,$  	,g      K   library/Exakat/Analyzer/Complete/SetClassPropertyDefinitionWithTypehint.php  rb  ١      3   library/Exakat/Analyzer/Complete/IsExtStructure.php  rb  Ҫ      3   library/Exakat/Analyzer/Complete/ReturnTypehint.php  rb  R甤      1   library/Exakat/Analyzer/Complete/SetCloneLink.php  rb        <   library/Exakat/Analyzer/Complete/SetClassAliasDefinition.php  rb  "u      6   library/Exakat/Analyzer/Complete/ExtendedTypehints.php
  rb
  q      7   library/Exakat/Analyzer/Complete/PhpNativeReference.php`  rb`  1;ۤ      ,   library/Exakat/Analyzer/Common/EnumUsage.php  rb  %5      1   library/Exakat/Analyzer/Common/NamespaceUsage.php  rb  Aޤ      '   library/Exakat/Analyzer/Common/Type.php  rb  ss      5   library/Exakat/Analyzer/Common/ConstantDefinition.phpO  rbO  Lr~      3   library/Exakat/Analyzer/Common/PhpFunctionUsage.php  rb  ;      2   library/Exakat/Analyzer/Common/ClassDefinition.php  rb  <+T&      1   library/Exakat/Analyzer/Common/InterfaceUsage.php	  rb	  t/      -   library/Exakat/Analyzer/Common/ClassUsage.phpp  rbp  (      -   library/Exakat/Analyzer/Common/WithoutTry.php  rb        5   library/Exakat/Analyzer/Common/FunctionDefinition.php  rb  K6      /   library/Exakat/Analyzer/Common/KeywordUsage.php`  rb`  ?      7   library/Exakat/Analyzer/Common/MultipleDeclarations.php@  rb@  `      0   library/Exakat/Analyzer/Common/UsedDirective.php  rb  0r      0   library/Exakat/Analyzer/Common/PropertyUsage.php  rb  S~      7   library/Exakat/Analyzer/Common/FunctionDefaultValue.phpZ  rbZ  ೤      6   library/Exakat/Analyzer/Common/InterfaceDefinition.php  rb  ]      -   library/Exakat/Analyzer/Common/TraitUsage.php  rb  |      2   library/Exakat/Analyzer/Common/MethodcallUsage.phpg  rbg        5   library/Exakat/Analyzer/Common/ClassConstantUsage.php  rb  \      /   library/Exakat/Analyzer/Common/IsSubclassOf.php8  rb8  ܤ      8   library/Exakat/Analyzer/Common/UsesFrameworkFunction.php>  rb>  ':      0   library/Exakat/Analyzer/Common/UsesFramework.php  rb  &b      '   library/Exakat/Analyzer/Common/None.php  rb  U4      ,   library/Exakat/Analyzer/Common/Extension.php@  rb@  /W      0   library/Exakat/Analyzer/Common/ConstantUsage.phpg  rbg  Kڤ      8   library/Exakat/Analyzer/Common/UsesFrameworkConstant.phpI  rbI  ÓB>      0   library/Exakat/Analyzer/Common/FunctionUsage.php  rb  D      .   library/Exakat/Analyzer/Common/MethodUsage.phpC  rbC  N/      -   library/Exakat/Configsource/DefaultConfig.phpX  rbX  ,٤      /   library/Exakat/Configsource/DatastoreConfig.php  rb  3>      +   library/Exakat/Configsource/CommandLine.phpA  rbA        +   library/Exakat/Configsource/EmptyConfig.php  rb  R      3   library/Exakat/Configsource/DotExakatYamlConfig.phpG3  rbG3        -   library/Exakat/Configsource/RulesetConfig.php.  rb.  :ឤ      &   library/Exakat/Configsource/Config.phpZ  rbZ  g      -   library/Exakat/Configsource/ProjectConfig.php5  rb5  y"      ,   library/Exakat/Configsource/ExakatConfig.php;  rb;         /   library/Exakat/Configsource/DotExakatConfig.php  rb  mwˤ      )   library/Exakat/Configsource/EnvConfig.php  rb  $a         library/Exakat/Data/CakePHP.php`  rb`  7:)F      "   library/Exakat/Data/Dictionary.php  rb  i!Ĥ      !   library/Exakat/Data/Collector.php  rb  hF         library/Exakat/Data/ZendF.php  rb            library/Exakat/Data/ZendF3.php>  rb>  d)|          library/Exakat/Data/Composer.phpj  rbj           library/Exakat/Data/ZendF2.php  rb  YCB         library/Exakat/Data/Data.php  rb  Ȭ         library/Exakat/Data/Methods.php5  rb5  S         library/Exakat/Dump/Dump1.phpn  rbn  TN"         library/Exakat/Dump/Dump2.phpXx  rbXx  ]K?         library/Exakat/Dump/Dump.php  rb  q	      1   library/Exakat/Exceptions/NoStructureForTable.php;  rb;  ΢      5   library/Exakat/Exceptions/AnotherProcessIsRunning.php  rb  ?      $   library/Exakat/Exceptions/NoDump.php  rb        *   library/Exakat/Exceptions/NoSuchLoader.php  rb  y      )   library/Exakat/Exceptions/NoPhpBinary.php?  rb?  JU:~      .   library/Exakat/Exceptions/ProjectNotInited.php  rb  J      (   library/Exakat/Exceptions/NoSuchFile.phpf  rbf  v<      *   library/Exakat/Exceptions/DSLException.php  rb  X|y      +   library/Exakat/Exceptions/ProjectNeeded.phpc  rbc  0vƲ      -   library/Exakat/Exceptions/ProjectTooLarge.php  rb  :      0   library/Exakat/Exceptions/InvalidProjectName.phpd  rbd  ׽-      (   library/Exakat/Exceptions/VcsSupport.php  rb  9      7   library/Exakat/Exceptions/WrongNumberOfColsForAHash.phpp  rbp  s      0   library/Exakat/Exceptions/NoRecognizedTokens.php  rb  w      (   library/Exakat/Exceptions/UnknownDsl.php"  rb"  #]W      '   library/Exakat/Exceptions/NoSuchDir.phpa  rba  B.j      +   library/Exakat/Exceptions/NoSuchProject.phpd  rbd  ̚0      .   library/Exakat/Exceptions/InvalidPHPBinary.php  rb  BW      0   library/Exakat/Exceptions/NeedsAnalyzerThema.phpO  rbO  C7      '   library/Exakat/Exceptions/NoDumpYet.php  rb  8Ju      ,   library/Exakat/Exceptions/InaptPHPBinary.php  rb  Z      .   library/Exakat/Exceptions/GremlinException.php  rb  V~      4   library/Exakat/Exceptions/WrongNumberOfArguments.phpv  rbv        ,   library/Exakat/Exceptions/QueryException.phpd  rbd  <N      '   library/Exakat/Exceptions/LoadError.phpr  rbr  zؤ      /   library/Exakat/Exceptions/NotProjectInGraph.php  rb  P;
      (   library/Exakat/Exceptions/MustBeADir.phpv  rbv        -   library/Exakat/Exceptions/HelperException.php  rb        3   library/Exakat/Exceptions/UnknownGremlinVersion.phpC  rbC  R      0   library/Exakat/Exceptions/WrongParameterType.phpm  rbm  W      -   library/Exakat/Exceptions/NoFileToProcess.php  rb  0      +   library/Exakat/Exceptions/NoSuchRuleset.php  rb  bѤ      )   library/Exakat/Exceptions/UnknownCase.phpK  rbK  BQ      -   library/Exakat/Exceptions/NoCodeInProject.phpc  rbc  g      &   library/Exakat/Exceptions/VcsError.phpb  rbb  AѓV      )   library/Exakat/Exceptions/MustBeAFile.phpr  rbr  ~      ,   library/Exakat/Exceptions/NoSuchAnalyzer.php>  rb>  T٤      *   library/Exakat/Exceptions/NoPrecedence.phpi  rbi  Ӓ      *   library/Exakat/Exceptions/NoSuchReport.php*  rb*  8]>d      )   library/Exakat/Exceptions/MissingFile.php  rb  .3%դ      ,   library/Exakat/Exceptions/MissingGremlin.php[  rb[  ;(f      /   library/Exakat/Exceptions/NoJobqueueStarted.php[  rb[  Xܤ      *   library/Exakat/Exceptions/NoSuchFormat.php  rb  P      $   library/Exakat/Graph/Tinkergraph.php(  rb(  H~V         library/Exakat/Graph/.DS_Store  rb  !U      "   library/Exakat/Graph/NoGremlin.php  rb  $ɤ      "   library/Exakat/Graph/GSNeo4jV3.php%  rb%  r         library/Exakat/Graph/Graph.php
  rb
  ]          library/Exakat/Graph/GSNeo4j.phpH*  rbH*  Q$w      &   library/Exakat/Graph/TinkergraphV3.php#  rb#  p      *   library/Exakat/Graph/Helpers/Websocket.phpB  rbB  F      -   library/Exakat/Graph/Helpers/GraphResults.phpI  rbI  v5[      +   library/Exakat/Graph/Helpers/GraphsonV3.phpF  rbF  [      $   library/Exakat/Fileset/Filenames.php  rb  U         library/Exakat/Fileset/All.php/  rb/  x?*         library/Exakat/Fileset/Set.php  rb         %   library/Exakat/Fileset/IgnoreDirs.php	  rb	  ;#T      &   library/Exakat/Fileset/IncludeDirs.php  rb  |'      %   library/Exakat/Fileset/Namespaces.php<  rb<  ~Ť      "   library/Exakat/Fileset/Fileset.php{  rb{  !cm      )   library/Exakat/Fileset/FileExtensions.phps  rbs  ߧB          library/Exakat/Loader/Loader.php  rb  ׯ      #   library/Exakat/Loader/Collector.php  rb  u2      '   library/Exakat/Loader/SplitGraphson.phpN  rbN  i         library/Exakat/Loader/None.phpX  rbX  h          library/Exakat/Vcs/Mercurial.php  rb  ϭ
1         library/Exakat/Vcs/Bazaar.php~  rb~  SV<6         library/Exakat/Vcs/Git.php)  rb)  0w         library/Exakat/Vcs/Svn.php  rb  ֟         library/Exakat/Vcs/Zip.php  rb  &         library/Exakat/Vcs/SevenZ.php	  rb	  +q}         library/Exakat/Vcs/Rar.php	  rb	  .>         library/Exakat/Vcs/Symlink.phpT  rbT  .i[g         library/Exakat/Vcs/Cvs.phpJ  rbJ  {I         library/Exakat/Vcs/Tarbz.php  rb  z         library/Exakat/Vcs/Composer.php  rb  1t         library/Exakat/Vcs/Vcs.php  rb  #U         library/Exakat/Vcs/None.php  rb  \2         library/Exakat/Vcs/Targz.php@  rb@  d         library/Exakat/Vcs/Copy.php|  rb|  C      !   library/Exakat/Stubs/StubJson.php"  rb"  '*ޤ      '   library/Exakat/Stubs/StubsInterface.php  rb  KΤ         library/Exakat/Stubs/Stubs.php
  rb
        #   library/Exakat/Stubs/PdffReader.php-`  rb-`  MV튤          library/Exakat/Stubs/StubIni.php?  rb?  	:٤         library/Exakat/Log/Timing.php  rb  Xpۤ         library/Exakat/Log/Log.php  rb           library/Exakat/Query/.DS_Store   rb   {'٤         library/Exakat/Query/Query2.php!  rb!  Rw      !   library/Exakat/Query/QueryDoc.php2  rb2  %Œ         library/Exakat/Query/Query.php"  rb"  )      (   library/Exakat/Query/DSL/IsLowercase.php  rb  UL      ,   library/Exakat/Query/DSL/GoToInstruction.php  rb  ;Ф      ,   library/Exakat/Query/DSL/IsNotEmptyArray.php(  rb(  դ      /   library/Exakat/Query/DSL/CompatibleWithType.php$  rb$  S|      +   library/Exakat/Query/DSL/AtomFunctionIs.phpv  rbv  7*      (   library/Exakat/Query/DSL/FollowParAs.php  rb  왤      0   library/Exakat/Query/DSL/GoToFirstExpression.php  rb  	y      +   library/Exakat/Query/DSL/CollectExtends.php  rb  0Z0      ,   library/Exakat/Query/DSL/IsNotLocalClass.php  rb  m{Τ      +   library/Exakat/Query/DSL/SamePropertyAs.php  rb  F[U(      *   library/Exakat/Query/DSL/OutIsWithLink.phpe  rbe  5n      .   library/Exakat/Query/DSL/AtomInsideNoBlock.php  rb  6ǽ%      &   library/Exakat/Query/DSL/SelectMap.php_  rb_  _      ,   library/Exakat/Query/DSL/FullnspathIsNot.php  rb  x,Ǥ      #   library/Exakat/Query/DSL/HasOut.php  rb  ;      1   library/Exakat/Query/DSL/ProcessDereferencing.phpC  rbC  ǓU      /   library/Exakat/Query/DSL/FullcodeVariableIs.php  rb  %      &   library/Exakat/Query/DSL/IsLiteral.php  rb  [w      %   library/Exakat/Query/DSL/GoToFile.php/  rb/  vr&      '   library/Exakat/Query/DSL/HasNoTrait.php3  rb3  OƖ      #   library/Exakat/Query/DSL/Ignore.php  rb  x      ,   library/Exakat/Query/DSL/NotImplementing.php-  rb-  +      ,   library/Exakat/Query/DSL/HasNotAttribute.php  rb  &Ф      .   library/Exakat/Query/DSL/GoToAllImplements.php  rb  ZYHۤ      4   library/Exakat/Query/DSL/HasNoFunctionDefinition.phpc  rbc        *   library/Exakat/Query/DSL/CollectValues.php^  rb^  3f      4   library/Exakat/Query/DSL/HasNoCountedInstruction.php  rb  (b      *   library/Exakat/Query/DSL/IsNotArgument.php%  rb%  {      -   library/Exakat/Query/DSL/NoAnalyzerInside.php  rb  ,>      )   library/Exakat/Query/DSL/IsGlobalCode.php  rb  Z[R      8   library/Exakat/Query/DSL/NoAtomWithoutPropertyInside.php  rb  (y;      '   library/Exakat/Query/DSL/IsLessHash.php  rb  S*      $   library/Exakat/Query/DSL/TokenIs.phpq  rbq  ֋      "   library/Exakat/Query/DSL/.DS_Store  rb  j m      1   library/Exakat/Query/DSL/NoAtomPropertyInside.php{  rb{  5      +   library/Exakat/Query/DSL/IsNotEmptyBody.phpt  rbt  -      2   library/Exakat/Query/DSL/NoInterfaceDefinition.php-  rb-  h      $   library/Exakat/Query/DSL/AddAtom.php  rb  Q      2   library/Exakat/Query/DSL/HasNoNamedInstruction.php  rb  !      %   library/Exakat/Query/DSL/HasNoOut.php"  rb"  B      +   library/Exakat/Query/DSL/SaveNullableAs.php)  rb)  e      #   library/Exakat/Query/DSL/Values.phpY  rbY        1   library/Exakat/Query/DSL/IsNotPropertyDefined.php  rb  3\5      -   library/Exakat/Query/DSL/HasChildWithRank.php  rb  s"      (   library/Exakat/Query/DSL/GoToExtends.php	  rb	        (   library/Exakat/Query/DSL/OutWithRank.php  rb  Ť      2   library/Exakat/Query/DSL/HasConstantDefinition.php7  rb7  E      +   library/Exakat/Query/DSL/HasNextSibling.phpu  rbu  ~$f      0   library/Exakat/Query/DSL/CountArrayDimension.php  rb  VW      %   library/Exakat/Query/DSL/HasTrait.php.  rb.  Sg      &   library/Exakat/Query/DSL/GoToClass.php]  rb]  BH      *   library/Exakat/Query/DSL/HasNoTryCatch.php4  rb4  Τ      2   library/Exakat/Query/DSL/NotCompatibleWithType.php  rb  L.      "   library/Exakat/Query/DSL/HasNo.phpc  rbc  M      )   library/Exakat/Query/DSL/GoToFunction.php  rb  0Q      ,   library/Exakat/Query/DSL/ClassDefinition.php  rb  Y          library/Exakat/Query/DSL/Has.php  rb  U      5   library/Exakat/Query/DSL/HasNoClassInterfaceTrait.phpe  rbe  f7      /   library/Exakat/Query/DSL/HasClassDefinition.php  rb  h&      )   library/Exakat/Query/DSL/IsLocalClass.php  rb        '   library/Exakat/Query/DSL/GoToParent.php  rb  d>      !   library/Exakat/Query/DSL/InIs.php  rb  ~$\G      %   library/Exakat/Query/DSL/OutIsNot.phpd  rbd  A      3   library/Exakat/Query/DSL/HasInterfaceDefinition.php'  rb'  WG      4   library/Exakat/Query/DSL/GoToParameterDefinition.phpm  rbm  Z2      /   library/Exakat/Query/DSL/GoToAllDefinitions.phpg  rbg  M;      #   library/Exakat/Query/DSL/IsUsed.php&  rb&  [Ka      '   library/Exakat/Query/DSL/HasNoUsage.phpk  rbk  <      #   library/Exakat/Query/DSL/AtomIs.phpq  rbq  T:      /   library/Exakat/Query/DSL/NoArgumentWithName.php  rb  g      2   library/Exakat/Query/DSL/HasFunctionDefinition.php:  rb:  ]      .   library/Exakat/Query/DSL/NotSameTypehintAs.php  rb   ;      ,   library/Exakat/Query/DSL/GoToAllChildren.php#  rb#  u      *   library/Exakat/Query/DSL/IsNotNullable.php  rb        .   library/Exakat/Query/DSL/IsInExtendedClass.php  rb        #   library/Exakat/Query/DSL/Unique.php  rb  ؔ<      -   library/Exakat/Query/DSL/PreviousSiblings.php  rb        .   library/Exakat/Query/DSL/HasPropertyInside.php
  rb
  "      .   library/Exakat/Query/DSL/CollectImplements.phpQ  rbQ        -   library/Exakat/Query/DSL/CollectVariables.php  rb  l=K      '   library/Exakat/Query/DSL/GroupCount.php  rb  _Z      +   library/Exakat/Query/DSL/FunctionInside.phpZ  rbZ  J      +   library/Exakat/Query/DSL/IsNotMixedcase.php  rb  wSh      "   library/Exakat/Query/DSL/GetWs.php  rb  פ      *   library/Exakat/Query/DSL/AnalyzerIsNot.php  rb  籤      *   library/Exakat/Query/DSL/NoDelimiterIs.php  rb  B      (   library/Exakat/Query/DSL/FollowAlias.phpp  rbp  HŤ      "   library/Exakat/Query/DSL/HasIn.php  rb  1l      "   library/Exakat/Query/DSL/Dedup.php  rb  1h      2   library/Exakat/Query/DSL/HasNoVariadicArgument.php)  rb)  #¤      &   library/Exakat/Query/DSL/HasNoLoop.php\  rb\  0Z      -   library/Exakat/Query/DSL/GoToLiteralValue.php  rb  奩      #   library/Exakat/Query/DSL/IsHash.phpB  rbB  !d+      (   library/Exakat/Query/DSL/GroupFilter.php  rb  *n      $   library/Exakat/Query/DSL/RegexIs.php  rb  Ԥ      (   library/Exakat/Query/DSL/HasFunction.php`  rb`  &m$      /   library/Exakat/Query/DSL/FunctionDefinition.php:  rb:  /D      #   library/Exakat/Query/DSL/ToDrop.php  rb  G{          library/Exakat/Query/DSL/Raw.php  rb  Ә      )   library/Exakat/Query/DSL/FullnspathIs.phpO  rbO  3&r      -   library/Exakat/Query/DSL/NoFullcodeInside.php  rb  Ĭ      0   library/Exakat/Query/DSL/HasNoClassInterface.phpb  rbb  .      (   library/Exakat/Query/DSL/HasNoIfthen.php5  rb5  "
      "   library/Exakat/Query/DSL/OutIs.php  rb  *      ,   library/Exakat/Query/DSL/PreviousSibling.phpk  rbk  
-ɤ      #   library/Exakat/Query/DSL/InIsIE.php  rb        ,   library/Exakat/Query/DSL/NoUseDefinition.php+  rb+        +   library/Exakat/Query/DSL/FunctioncallIs.php  rb  i{      $   library/Exakat/Query/DSL/IsAnyOf.phpX  rbX  {d$j      2   library/Exakat/Query/DSL/CodeIsPositiveInteger.php6  rb6  b?      0   library/Exakat/Query/DSL/HasVariadicArgument.php"  rb"  \      &   library/Exakat/Query/DSL/HasParent.phpO  rbO  uY      $   library/Exakat/Query/DSL/HasLoop.phpX  rbX  r}      "   library/Exakat/Query/DSL/SetWs.php@  rb@  g      0   library/Exakat/Query/DSL/IsComplexExpression.php  rb  *$      *   library/Exakat/Query/DSL/PreviousCalls.php  rb  AD          library/Exakat/Query/DSL/DSL.phpj@  rbj@  km      4   library/Exakat/Query/DSL/HasNoConstantDefinition.php@  rb@  e      (   library/Exakat/Query/DSL/IdenticalTo.php  rb  ݂      (   library/Exakat/Query/DSL/HasChildren.php:  rb:  
P      )   library/Exakat/Query/DSL/IsNotLiteral.php  rb  ٴa      -   library/Exakat/Query/DSL/HasNoInstruction.php  rb  ]z      -   library/Exakat/Query/DSL/FollowExpression.php  rb  J$      .   library/Exakat/Query/DSL/IsPropertyDefined.phpn  rbn  <춤      &   library/Exakat/Query/DSL/GoToArray.phpc  rbc  );      +   library/Exakat/Query/DSL/HasNoInterface.php:  rb:  e      4   library/Exakat/Query/DSL/ProcessIndentingAverage.php
  rb
  >
&      /   library/Exakat/Query/DSL/HasTraitDefinition.php  rb  >C      !   library/Exakat/Query/DSL/Trim.php  rb  OBʤ      ,   library/Exakat/Query/DSL/GoToOtherBranch.php  rb  ݽ      $   library/Exakat/Query/DSL/OutIsIE.phps  rbs  Zä      *   library/Exakat/Query/DSL/PropertyIsNot.phpR
  rbR
  g~      1   library/Exakat/Query/DSL/IsNotInheritedMethod.php  rb        /   library/Exakat/Query/DSL/GoToParameterUsage.php  rb  gE      )   library/Exakat/Query/DSL/NotExtending.php  rb  [u      %   library/Exakat/Query/DSL/AddEFrom.php  rb  ț      &   library/Exakat/Query/DSL/SaveOutAs.php  rb  S%*      *   library/Exakat/Query/DSL/HasClassTrait.phpc  rbc  y      +   library/Exakat/Query/DSL/FullcodeLength.php  rb  45      &   library/Exakat/Query/DSL/IsVisible.phpR  rbR  8      #   library/Exakat/Query/DSL/CodeIs.phpb	  rbb	  t      *   library/Exakat/Query/DSL/GoToInterface.php9  rb9  }      +   library/Exakat/Query/DSL/IsNotUppercase.php  rb  [e      *   library/Exakat/Query/DSL/CollectTraits.php  rb  j      +   library/Exakat/Query/DSL/HasInstruction.php  rb  PZte      %   library/Exakat/Query/DSL/GoToLoop.phpZ  rbZ        *   library/Exakat/Query/DSL/GoToNamespace.phpF  rbF  8      (   library/Exakat/Query/DSL/ReturnCount.php  rb  @;       %   library/Exakat/Query/DSL/Optional.php  rb  *8      0   library/Exakat/Query/DSL/SamePropertyAsArray.php	  rb	  #      (   library/Exakat/Query/DSL/IsUppercase.php  rb  ä      &   library/Exakat/Query/DSL/NextCalls.php  rb  we      0   library/Exakat/Query/DSL/InterfaceDefinition.php  rb  hWG      &   library/Exakat/Query/DSL/AtomIsNot.phpZ  rbZ  oA9      &   library/Exakat/Query/DSL/ToResults.php  rb  X_큤      1   library/Exakat/Query/DSL/AtomInsideExpression.php  rb  :      #   library/Exakat/Query/DSL/IsMore.php  rb  쇠      ,   library/Exakat/Query/DSL/HasNoComparison.php=  rb=  NmF      '   library/Exakat/Query/DSL/FullcodeIs.php  rb  _      &   library/Exakat/Query/DSL/Extending.php  rb  #Ҟ      $   library/Exakat/Query/DSL/IsEqual.php  rb  (\      .   library/Exakat/Query/DSL/FunctioncallIsNot.php  rb  _      +   library/Exakat/Query/DSL/FullcodeInside.php  rb  w      '   library/Exakat/Query/DSL/AnalyzerIs.php  rb        ,   library/Exakat/Query/DSL/HasNoDefinition.php  rb  %Ӱ      4   library/Exakat/Query/DSL/GoToClassInterfaceTrait.phpc  rbc  HK      .   library/Exakat/Query/DSL/NotSamePropertyAs.phps  rbs  '         library/Exakat/Query/DSL/Is.php
  rb
  {	      (   library/Exakat/Query/DSL/HasTryCatch.php0  rb0  [v      /   library/Exakat/Query/DSL/OutWithoutLastRank.php`  rb`  tѤ      '   library/Exakat/Query/DSL/GoToMethod.phpN  rbN  T      *   library/Exakat/Query/DSL/ProcessLevels.php  rb  ä      *   library/Exakat/Query/DSL/OtherSiblings.php  rb  *[      %   library/Exakat/Query/DSL/Property.php}  rb}  9#.      '   library/Exakat/Query/DSL/TokenIsNot.php{  rb{  dV      '   library/Exakat/Query/DSL/IsArgument.php  rb  m      "   library/Exakat/Query/DSL/Count.php  rb  O      *   library/Exakat/Query/DSL/HasAtomInside.phpF  rbF  D      1   library/Exakat/Query/DSL/IsNotClassCompatible.php  rb  A      *   library/Exakat/Query/DSL/DuplicateNode.php  rb  ZE<	      3   library/Exakat/Query/DSL/IsNotExtendingComposer.phpL  rbL  $Ǥ      1   library/Exakat/Query/DSL/GoToAllParentsTraits.php:  rb:  ٤      (   library/Exakat/Query/DSL/NextSibling.php  rb  k^      ,   library/Exakat/Query/DSL/IsMissingOrNull.php  rb        2   library/Exakat/Query/DSL/AtomInsideNoAnonymous.php  rb  E|      /   library/Exakat/Query/DSL/GoToClassInterface.php`  rb`  󉉤      .   library/Exakat/Query/DSL/CheckTypeWithAtom.phpY  rbY  *N      )   library/Exakat/Query/DSL/NoAtomInside.php  rb  դ      '   library/Exakat/Query/DSL/IsMoreHash.php  rb  rWE      #   library/Exakat/Query/DSL/Filter.php  rb  9G      *   library/Exakat/Query/DSL/GoToAllTraits.phpN  rbN  uo=K      $   library/Exakat/Query/DSL/IsAllOf.phpY  rbY  ˤ      -   library/Exakat/Query/DSL/NoDelimiterIsNot.php  rb   ߤ      )   library/Exakat/Query/DSL/IsReassigned.php  rb  ,Ӥ      .   library/Exakat/Query/DSL/NoTraitDefinition.php%  rb%  -      +   library/Exakat/Query/DSL/GoToAllParents.php  rb  %)j      ,   library/Exakat/Query/DSL/GetStringLength.php  rb  >K      (   library/Exakat/Query/DSL/GoToAllElse.php  rb  H      #   library/Exakat/Query/DSL/IsThis.php  rb  Yb      )   library/Exakat/Query/DSL/GetNameInFNP.php  rb  f      $   library/Exakat/Query/DSL/Command.phpq
  rbq
  $T      -   library/Exakat/Query/DSL/NoFunctionInside.php"  rb"   0      +   library/Exakat/Query/DSL/CollectMethods.php5  rb5        "   library/Exakat/Query/DSL/IsNot.php
  rb
  W.      (   library/Exakat/Query/DSL/GetVariable.php  rb  Ƥ      '   library/Exakat/Query/DSL/DSLFactory.php  rb  2ʤ      ,   library/Exakat/Query/DSL/NoChildWithRank.phpQ  rbQ  Jl      )   library/Exakat/Query/DSL/Implementing.php#  rb#  ä      -   library/Exakat/Query/DSL/MakeVariableName.phpJ  rbJ        /   library/Exakat/Query/DSL/AtomInsideMoreThan.php  rb  W      &   library/Exakat/Query/DSL/GoToTrait.php0  rb0  zU      +   library/Exakat/Query/DSL/GoToExpression.php  rb  Ś4      %   library/Exakat/Query/DSL/HasClass.php[  rb[  E#      '   library/Exakat/Query/DSL/HasNoCatch.php3  rb3  m	      '   library/Exakat/Query/DSL/AtomInside.phph  rbh  R샤      $   library/Exakat/Query/DSL/HasNoIn.php   rb   g      -   library/Exakat/Query/DSL/SaveMethodNameAs.php  rb  ]a!      .   library/Exakat/Query/DSL/HasClassInterface.php]  rb]  CD      #   library/Exakat/Query/DSL/Select.phpo  rbo  1      )   library/Exakat/Query/DSL/HasAttribute.php?  rb?  	      *   library/Exakat/Query/DSL/HasNoChildren.php  rb  :p      )   library/Exakat/Query/DSL/InitVariable.php!  rb!        (   library/Exakat/Query/DSL/SetProperty.php  rb  r      /   library/Exakat/Query/DSL/AtomInsideWithCall.php  rb  A       %   library/Exakat/Query/DSL/IsCalled.phpX  rbX  T      "   library/Exakat/Query/DSL/Range.php>  rb>  TH      &   library/Exakat/Query/DSL/ToCobbled.php  rb        )   library/Exakat/Query/DSL/NextSiblings.phpi  rbi  _g      &   library/Exakat/Query/DSL/HasIfthen.php1  rb1  5o      +   library/Exakat/Query/DSL/AnalyzerInside.php  rb  r      +   library/Exakat/Query/DSL/GoToClassTrait.phpe  rbe  1      -   library/Exakat/Query/DSL/CollectArguments.php  rb        /   library/Exakat/Query/DSL/VariableIsAssigned.php  rb  H      1   library/Exakat/Query/DSL/IsReferencedArgument.php	  rb	  p~      (   library/Exakat/Query/DSL/HasNoParent.phpW  rbW  ͈      3   library/Exakat/Query/DSL/AnalyzerInsideMoreThan.php  rb  5dˤ          library/Exakat/Query/DSL/_As.php  rb  %      +   library/Exakat/Query/DSL/SameTypehintAs.php  rb  w4      +   library/Exakat/Query/DSL/OutToParameter.php  rb  k      -   library/Exakat/Query/DSL/HasNoNextSibling.php~  rb~  +G      '   library/Exakat/Query/DSL/PropertyIs.php	  rb	  .Ѥ      3   library/Exakat/Query/DSL/AtomInsideNoDefinition.phpY  rbY            library/Exakat/Query/DSL/Not.php  rb  AM      $   library/Exakat/Query/DSL/NoQuery.php  rb  <      )   library/Exakat/Query/DSL/GoToAllRight.php  rb  [פ      #   library/Exakat/Query/DSL/AddETo.php  rb  i
      '   library/Exakat/Query/DSL/ToDropEdge.phpb  rbb  m*      '   library/Exakat/Query/DSL/HasNoClass.php^  rb^  O`T      .   library/Exakat/Query/DSL/CollectContainers.php  rb  H      *   library/Exakat/Query/DSL/FullcodeIsNot.php  rb  ^D      &   library/Exakat/Query/DSL/StopQuery.php  rb  b9+      +   library/Exakat/Query/DSL/SavePropertyAs.php
  rb
  Š      &   library/Exakat/Query/DSL/CodeIsNot.php	  rb	  ܣߤ      )   library/Exakat/Query/DSL/GoToTypehint.php  rb  i      !   library/Exakat/Query/DSL/Back.php  rb  *Ӥ      (   library/Exakat/Query/DSL/FollowCalls.php  rb        $   library/Exakat/Query/DSL/CountBy.php5  rb5  2      -   library/Exakat/Query/DSL/GoToCurrentScope.phpL  rbL  ,:      )   library/Exakat/Query/DSL/NoCodeInside.php  rb  H      ,   library/Exakat/Query/DSL/HasNoClassTrait.phps  rbs        '   library/Exakat/Query/DSL/RegexIsNot.php  rb  S¡#      '   library/Exakat/Query/DSL/CollectOut.php  rb  GC      )   library/Exakat/Query/DSL/DistinctFrom.php  rb  W؋'      )   library/Exakat/Query/DSL/HasInterface.php7  rb7  Z      '   library/Exakat/Query/DSL/IsNullable.php  rb  V      *   library/Exakat/Query/DSL/InterfaceLike.php  rb  ۏeO      (   library/Exakat/Query/DSL/FollowValue.php  rb  Vz      .   library/Exakat/Query/DSL/NoClassDefinition.php	  rb	  Y      +   library/Exakat/Query/DSL/IsNotLowercase.php  rb  G1      .   library/Exakat/Query/DSL/IsClassCompatible.phpG	  rbG	  6      (   library/Exakat/Query/DSL/CollectKeys.php[  rb[        #   library/Exakat/Query/DSL/IsLess.php  rb  ӵD      -   library/Exakat/Query/DSL/CollectTypehints.php  rb  j\      &   library/Exakat/Query/DSL/IsNotHash.php  rb  -Ė      )   library/Exakat/Query/DSL/IsNotIgnored.php  rb        $   library/Exakat/Query/DSL/InIsNot.phpU  rbU  ݠ      9   library/Exakat/Query/DSL/NoAnalyzerInsideWithProperty.php  rb  :Hd      +   library/Exakat/Query/DSL/GoToImplements.php  rb  Wׁ      *   library/Exakat/Query/DSL/HasNoFunction.phpc  rbc  DFͤ          library/Exakat/Tasks/Onefile.php?  rb?  bw         library/Exakat/Tasks/Update.php  rb   ܤ         library/Exakat/Tasks/Load.php rb ><          library/Exakat/Tasks/Catalog.php	  rb	  		         library/Exakat/Tasks/.DS_Store(  rb(  9          library/Exakat/Tasks/Install.php  rb  YZu         library/Exakat/Tasks/Dump.phpW rbW b          library/Exakat/Tasks/Analyze.php*1  rb*1  q          library/Exakat/Tasks/Results.php@  rb@  uK         library/Exakat/Tasks/Show.php  rb  2Z!J      !   library/Exakat/Tasks/Baseline.phpY  rbY  ݼ         library/Exakat/Tasks/Files.php)  rb)  פ      "   library/Exakat/Tasks/Anonymize.php7  rb7  c         library/Exakat/Tasks/Export.php  rb  Q         library/Exakat/Tasks/Doctor.php>A  rb>A  W         library/Exakat/Tasks/Tasks.php  rb  RѤ      .   library/Exakat/Tasks/FindExternalLibraries.phpG$  rbG$  䓤      $   library/Exakat/Tasks/Initproject.php#  rb#  7i_         library/Exakat/Tasks/Config.php  rb           library/Exakat/Tasks/Queue.php.  rb.  %ϖ          library/Exakat/Tasks/Upgrade.php  rb  W&,      #   library/Exakat/Tasks/TestCobble.php	  rb	            library/Exakat/Tasks/Project.phpF  rbF  `v         library/Exakat/Tasks/Test.php  rb  ݢ5         library/Exakat/Tasks/Help.php_  rb_  Fqz         library/Exakat/Tasks/Status.phpb  rbb  9F         library/Exakat/Tasks/Clean.php  rb  l$      "   library/Exakat/Tasks/EmptyTask.php  rb  uԤ(         library/Exakat/Tasks/Remove.php  rb  V7      !   library/Exakat/Tasks/Jobqueue.php$  rb$  s         library/Exakat/Tasks/Api.php	  rb	  gAb      1   library/Exakat/Tasks/LoadFinal/IsInIgnoredDir.php
  rb
  u1      (   library/Exakat/Tasks/LoadFinal/.DS_Store  rb  j m      9   library/Exakat/Tasks/LoadFinal/SpotPHPNativeConstants.php  rb  :q9      0   library/Exakat/Tasks/LoadFinal/FinishExtends.php  rb  &b      9   library/Exakat/Tasks/LoadFinal/FixFullnspathConstants.php  rb  Da      3   library/Exakat/Tasks/LoadFinal/FinishIsModified.php  rb  ǻӤ      9   library/Exakat/Tasks/LoadFinal/SpotPHPNativeFunctions.php  rb  V      ?   library/Exakat/Tasks/LoadFinal/SpotExtensionNativeFunctions.php  rb  rD      ,   library/Exakat/Tasks/LoadFinal/LoadFinal.php2  rb2  ծ      .   library/Exakat/Tasks/Helpers/BaselineStash.php  rb  z)j      )   library/Exakat/Tasks/Helpers/StubJson.phpE  rbE  a	      )   library/Exakat/Tasks/Helpers/Constant.php  rb  U*j      *   library/Exakat/Tasks/Helpers/AtomGroup.php  rb  L$      +   library/Exakat/Tasks/Helpers/IsModified.php  rb        &   library/Exakat/Tasks/Helpers/.DS_Store  rb  j m      (   library/Exakat/Tasks/Helpers/Nullval.phpC  rbC  Ce      %   library/Exakat/Tasks/Helpers/Lock.php  rb  	V      /   library/Exakat/Tasks/Helpers/AnonymousNames.php  rb  zo      &   library/Exakat/Tasks/Helpers/Php72.phpi$  rbi$  QZq      .   library/Exakat/Tasks/Helpers/AtomInterface.php  rb  
g      &   library/Exakat/Tasks/Helpers/Php73.phph$  rbh$  `      &   library/Exakat/Tasks/Helpers/Php71.phpi$  rbi$  -k      '   library/Exakat/Tasks/Helpers/Plugin.php.  rb.  ƖФ      0   library/Exakat/Tasks/Helpers/NestedCollector.php  rb  ap      &   library/Exakat/Tasks/Helpers/Php70.php/  rb/  J      )   library/Exakat/Tasks/Helpers/Encoding.php  rb  hA`      &   library/Exakat/Tasks/Helpers/Php74.phpf$  rbf$  zߝ      1   library/Exakat/Tasks/Helpers/ContextVariables.php  rb  0t      &   library/Exakat/Tasks/Helpers/Calls.php}  rb}  P#      2   library/Exakat/Tasks/Helpers/ClassTraitContext.php  rb        &   library/Exakat/Tasks/Helpers/IsPhp.php:  rb:  zy      &   library/Exakat/Tasks/Helpers/Php53.php#  rb#  <      +   library/Exakat/Tasks/Helpers/Precedence.php   rb   h\      (   library/Exakat/Tasks/Helpers/Context.php5
  rb5
  noѝ      %   library/Exakat/Tasks/Helpers/Atom.php4$  rb4$  Зˤ      '   library/Exakat/Tasks/Helpers/IsStub.phpC  rbC  :      )   library/Exakat/Tasks/Helpers/Property.phpv  rbv  
      &   library/Exakat/Tasks/Helpers/Php82.php/  rb/        &   library/Exakat/Tasks/Helpers/Php55.phpw%  rbw%  UЦ4      &   library/Exakat/Tasks/Helpers/Php54.php#  rb#  ѹF      '   library/Exakat/Tasks/Helpers/Strval.php@-  rb@-  Hp      &   library/Exakat/Tasks/Helpers/Php81.php/  rb/  F畤      &   library/Exakat/Tasks/Helpers/Php56.php/  rb/  פ      '   library/Exakat/Tasks/Helpers/Intval.phpg&  rbg&  9      &   library/Exakat/Tasks/Helpers/Php80.php/  rb/  v
      +   library/Exakat/Tasks/Helpers/Whitespace.php  rb  Vc      '   library/Exakat/Tasks/Helpers/IsRead.php0  rb0  I      (   library/Exakat/Tasks/Helpers/Boolval.php*  rb*  M'      $   library/Exakat/Tasks/Helpers/Php.phpd?  rbd?  ]      -   library/Exakat/Tasks/Helpers/ReportConfig.php  rb  n̤      &   library/Exakat/Tasks/Helpers/IsExt.php7  rb7  UX      ,   library/Exakat/Tasks/Helpers/Fullnspaths.php  rb  !j      *   library/Exakat/Tasks/Helpers/Sequences.php  rb           library/Exakat/Tasks/Report.php  rb  @kl         library/Exakat/Tasks/Stat.phpw	  rbw	  J1         library/Exakat/Tasks/Cobble.php  rb  K$s          library/Exakat/Tasks/CleanDb.php  rb  Ҥ          library/Exakat/Helpers/Timer.php  rb  Bڤ      )   library/Exakat/Helpers/SelectorParser.php  rb  0U      &   library/Exakat/Helpers/Definitions.php	  rb	  t      &   library/Exakat/Reports/Helpers/Dot.php  rb  vӤ      *   library/Exakat/Reports/Helpers/Results.phpe  rbe  a:      .   library/Exakat/Reports/Helpers/PhpCodeTree.php}  rb}        *   library/Exakat/Reports/Helpers/Mermaid.php  rb  {F      *   library/Exakat/Reports/Helpers/Section.php	  rb	  &      '   library/Exakat/Reports/Helpers/Pdff.php\  rb\  h^      ,   library/Exakat/Reports/Helpers/Highchart.phpq  rbq  p      '   library/Exakat/Reports/Helpers/Docs.php  rb  ~c      $   library/Exakat/Reports/Data/Data.php  rb  1      '   library/Exakat/Reports/Data/Appinfo.phpv  rbv  w      +   library/Exakat/Reports/Data/CloseNaming.phpF  rbF  /         data/directives/filter.json  rb  _         data/directives/fileupload.json  rb  ْ      &   data/directives/disable_functions.json  rb  -oV         data/directives/curl.json   rb   5         data/directives/date.json   rb   =qI         data/directives/ldap.json   rb   C#H         data/directives/session.json  rb  齄         data/directives/bcmath.json   rb   u|R         data/directives/apache.jsonB  rbB  8Z@         data/directives/errorlog.jsoni  rbi  I         data/directives/pdo.json   rb   '         data/directives/pcre.json   rb   hD         data/directives/trader.json   rb   i         data/directives/xcache.json4  rb4  Gb         data/directives/openssl.json   rb    פ         data/directives/file.json  rb  IƤ         data/directives/opcache.jsonz  rbz  %=         data/directives/mbstring.jsone  rbe  Y         data/directives/mailparse.json   rb   	         data/directives/env.json   rb   nj         data/directives/imagick.json   rb   OОf         data/directives/apc.json7  rb7  B螤         data/directives/amqp.json  rb  )g*         data/directives/assertion.jsonY  rbY  bK         data/directives/mail.json  rb  <_         data/directives/sqlite3.json   rb   r"         data/directives/ob.jsonw  rbw  t         data/directives/pgsql.json   rb   !.F         data/directives/enable_dl.json   rb   U         data/directives/sqlite.json   rb   ^O         data/directives/com.json9  rb9  wȿ         data/directives/mongo.json)  rb)  kw         data/directives/dba.jsong   rbg   cL         data/directives/standard.json:  rb:  4sl      !   data/directives/eaccelerator.jsont  rbt  ~ʇ         data/directives/intl.json  rb  [{y         data/directives/browscap.json   rb   +@         data/directives/geoip.json;  rb;  nD         data/directives/wincache.json  rb  6w         data/directives/ibase.json%  rb%  H         data/odbc.ini
  rb
           data/ast.inip  rbp  ҧr         data/sqlKeywords.inib   rbb   ͤ         data/pgsql.ini  rb  >_         data/swoole.ini  rb  Ѝ         data/imap.ini  rb  H֤         data/externallibraries.json$  rb$   r         data/sqlite.iniW	  rbW	  U         data/iis.ini  rb   )         data/mongodb.ini=  rb=  lù         data/extraWords.txt  rb           data/gearman.inii  rbi  !=         data/proctitle.ini   rb   9n         data/methods.sqlite X, rb X, ]         data/ob.ini  rb  L         data/wp_private_functions.ini  rb  ct)         data/csprng.ini   rb            data/mime_types.ini   rb   r9         data/errorMessageFunctions.ini  rb  z         data/recode.ini   rb   8	         data/gender.ini|   rb|   zWd         data/disable_functions.ini	  rb	  "Ta         data/weirdSpaces.ini  rb  ~Vl         data/zendf.sqlite  rb  n         data/vendors/laravel.inie   rbe   Oj         data/vendors/concrete5.pdff!ە rb!ە ,         data/vendors/joomla.pdffmarbma6         data/vendors/yii.pdff
% rb
% \)U         data/vendors/.DS_Store  rb  j m         data/vendors/codeigniter.ini  rb  PА         data/vendors/typo3.init   rbt   h<         data/vendors/typo3.pdffv rbv          data/vendors/wordpress.pdffi rbi Ȋ&         data/vendors/codeigniter.pdffs$ rbs$ kx#         data/vendors/wordpress.iniY  rbY  KL         data/vendors/yii.ini   rb   .         data/vendors/drupal.iniH   rbH   ZǤ         data/vendors/phalcon.iniI   rbI   h         data/vendors/sylius.pdff rb 6"         data/vendors/joomla.iniX  rbX  ר         data/vendors/fuel.iniF   rbF   b         data/vendors/ez.iniD   rbD   a         data/vendors/concrete5.ini  rb  
         data/vendors/symfony.inic   rbc   ~w         data/php_unittest.inik  rbk  8.~         data/deprecated.inix
  rbx
  [         data/zendmonitor.ini  rb  ʁ6ɤ         data/weakref.ini   rb   )	m         data/wasm.ini  rb  p$         data/php_classes.ini:	  rb:	  U         data/php_strange_names.ini  rb  hQCƤ         data/decimal.ini   rb   <u         data/protocols.json3  rb3  14Ȥ         data/php_incoming.ini   rb   $         data/ftp.ini  rb  2         data/fileinfo.ini-  rb-  H         data/soap.ini
  rb
  N      '   data/php_may_return_boolean_or_zero.ini  rb  芋         data/composer.sqlite A rb A Bb         data/Continents_en.ini  rb  a          data/.DS_Store(  rb(  /
         data/zlib.ini  rb  ~         data/imagick.iniE  rbE  3e         data/php_no_extension.iniP  rbP  K7	         data/eio.ini  rb  ޱ         data/core/ldap.pdff!  rb!  -W         data/core/session.pdff"I  rb"I  􄷤         data/core/pspell.pdffC8  rbC8  u         data/core/bcmath.pdff  rb  \6@         data/core/PDO.pdff[  rb[  I         data/core/readline.pdffr  rbr  )         data/core/gd.pdff rb l         data/core/pcre.pdffo1  rbo1  |N﷤         data/core/xml.pdffB  rbB  Bs}         data/core/shmop.pdff  rb  =2Z         data/core/mysqli.pdff rb          data/core/.DS_Store  rb  j m         data/core/filter.pdffO)  rbO)  ;qѤ         data/core/SimpleXML.pdffk]  rbk]  3V         data/core/pdo_pgsql.pdff}   rb}   uu         data/core/curl.pdff: rb: xT         data/core/date.pdffJ$ rbJ$ Hx-         data/core/odbc.pdffN  rbN  c         data/core/mbstring.pdff^  rb^  s;K         data/core/xmlwriter.pdffc  rbc  =         data/core/exif.pdffP  rbP           data/core/sysvsem.pdff
  rb
  )A8         data/core/dom.pdffS rbS eB         data/core/sodium.pdff0 rb0 c
         data/core/FFI.pdffQs  rbQs  84         data/core/gmp.pdff  rb  I         data/core/pdo_sqlite.pdff}   rb}   uu         data/core/PDO_ODBC.pdff}   rb}   uu         data/core/libxml.pdff  rb  :5         data/core/openssl.pdff  rb  O#         data/core/xmlreader.pdffKD  rbKD  `Q         data/core/xsl.pdff  rb  w         data/core/tokenizer.pdffC  rbC  y         data/core/bz2.pdff  rb  2         data/core/soap.pdffS  rbS  @5ä         data/core/gettext.pdff  rb  de         data/core/hash.pdffGL  rbGL  
jf         data/core/pgsql.pdffo: rbo: h         data/core/tidy.pdffg  rbg  J>         data/core/json.pdff&  rb&  1v3         data/core/zlib.pdffS  rbS  O>_         data/core/pdo_dblib.pdff}   rb}   uu         data/core/Core.pdff rb dR}         data/core/iconv.pdff!  rb!  $k         data/core/sqlite3.pdffhg  rbhg  >Y         data/core/pdo_mysql.pdff}   rb}   uu         data/core/ctype.pdff/  rb/  ]K֤         data/core/pcntl.pdff@  rb@  *gd         data/core/posix.pdff2?  rb2?  kS         data/core/fileinfo.pdff  rb  B         data/core/Phar.pdff rb          data/core/Zend OPcache.pdff	  rb	  4T         data/core/sockets.pdff+  rb+  ha̤         data/core/dba.pdff"  rb"  8-ܤ         data/core/Reflection.pdfff rbf 2         data/core/standard.pdffZ rbZ a         data/core/sysvmsg.pdff&  rb&  9}D         data/core/ftp.pdff5q  rb5q  (         data/core/calendar.pdffT4  rbT4  B٦ߤ         data/core/SPL.pdff)} rb)} LD
         data/core/intl.pdff! rb! ߸x         data/core/sysvshm.pdffY  rbY   Τ         data/lzf.ini   rb   ]         data/mssql.ini,  rb,  ,         data/enchant.ini{  rb{  Ӥ         data/NotHash.iniT  rbT  E         data/pcre.ini  rb  ^         data/drupal.sqlite ` rb ` o_         data/php_argument_port.ini rb 82         data/native_replacement.json   rb   C         data/php_functions.iniD  rbD           data/php_constant_returns.json<  rb<  6         data/php_keywords.ini  rb  /ˤ          data/php_interfaces_methods.json  rb  r         data/uopz.ini  rb  fY@n         data/Files2OS.ini   rb   Z          data/shmop.ini   rb   P         data/dom.ini  rb  r         data/sqlsrv.ini  rb  uP}         data/functions_minus_one.ini  rb  h         data/zmq.ini   rb   Pƒ         data/php_interfaces.ini{  rb{  ^         data/ffmpeg.ini1  rb1  h         data/php_filenames_arg.jsonL  rbL  ;Wͤ         data/redis.ini   rb   ~+         data/calendar.ini  rb  6Dm         data/inotify.iniy  rby  s@4         data/filter.ini`  rb`  zt         data/runkit.ini  rb  Ф         data/iconv.ini4  rb4  ,͵         data/ctype.ini}  rb}  z         data/cairo.ini7  rb7  $         data/php_prints.ini  rb  H         data/sqlite3.ini  rb  m-         data/php_enums.ini
   rb
   7ܤ         data/tokenizer.iniA  rbA  j         data/aliases.inij  rbj  Ҥ         data/shouldDisableFunction.json  rb  s?{}         data/xdebug.ini	  rb	  B#         data/lapack.ini   rb   !st         data/environment_vars.iniu   rbu   #a         data/pear.iniF  rbF  ʤ      &   data/security_vulnerable_functions.iniK  rbK  ˬS         data/mysqli.iniD  rbD  j]         data/libsodium.ini  rb  Qkk٤         data/parle.ini  rb  ZP         data/query_methods.json   rb   VF         data/php_web_variables.ini}   rb}   ⺤         data/com.iniz	  rbz	  oIj         data/resource_creation.ini`  rb`  5+         data/ports.ini(  rb(  6         data/yaml.ini  rb  #.B         data/php_distribution_53.inie  rbe  {!         data/audit_names.ini)Y  rb)Y  aߤ         data/ds.inig  rbg  M         data/memcached.iniZ  rbZ           data/php7_relaxed_keyword.ini  rb  e*         data/noscream_functions.json8   rb8   ed         data/gnupg.ini  rb  b         data/configure.json  rb  .lƤ         data/simplexml.ini   rb            data/xxtea.ini   rb   |         data/xmlreader.jsonI  rbI  i.y         data/php_scalar_types.ini
  rb
  Q#         data/wikidiff2.ini   rb            data/hash_length.jsonW  rbW  GmG         data/string.ini-  rb-  ͇ x         data/xhprof.iniB  rbB  y3         data/php_variables.ini@  rb@  	         data/thankyou.ini\  rb\  ަ뭤         data/posix.iniJ  rbJ           data/mbstring_encodings.iniP  rbP  i         data/zbarcode.ini   rb   .a<         data/ev.iniu  rbu  p         data/zookeeper.ini  rb  ѼnŤ         data/compatibility.json  rb  Sg@         data/exif.ini  rb  op         data/unicode_blocks.json  rb  z^         data/stats.ini	  rb	  ލi         data/ldap.ini  rb  )         data/snmp.iniP  rbP  N	퓤         data/sphinx.ini3  rb3  阤         data/openssl.ini  rb  rd         data/hash_algos.iniq  rbq   ᮹         data/gmp.ini  rb  #z         data/ignore_files.ini  rb  %-         data/fpm.ini   rb   Kt         data/xattr.iniM  rbM  gh         data/xmlwriter.json  rb  nnOK         data/xcache.ini  rb  U         data/vips.ini  rb  ס         data/dio.iniK  rbK  P@         data/zendf2.sqlite P rb P =         data/gettext.ini_  rb_  }         data/fann.ini   rb   $ˤ         data/msgpack.ini  rb           data/license.inia   rba   ;I         data/owasp.top10.jsonp  rbp           data/eaccelerator.ini  rb  ٳ         data/date.ini9  rb9  tvI         data/mysql.ini  rb  ٸ         data/math.iniw  rbw  DÁ         data/v8js.iniB  rbB  T         data/password.ini  rb  0         data/sem.ini  rb  !         data/php_traits.ini   rb   }         data/apache.iniO  rbO  ʦ         data/extensions/jsonpath.pdff  rb  '         data/extensions/yar.pdffz  rbz  M(         data/extensions/mongodb.pdff5l rb5l ,         data/extensions/opencensus.pdff:R  rb:R  oT         data/extensions/gender.pdff'  rb'  s         data/extensions/protobuf.pdff_6 rb_6 }>         data/extensions/trader.pdff3 rb3 DB5         data/extensions/.DS_Store  rb  j m         data/extensions/xxtea.pdff  rb  FŤ         data/extensions/eio.pdff  rb  Ĥ         data/extensions/mysqlnd.pdff   rb   0J         data/extensions/mailparse.pdff4  rb4  q         data/extensions/xattr.pdffI  rbI           data/extensions/tensor.pdff rb 2         data/extensions/imagick.pdff/ rb/ Lޤ         data/extensions/mcrypt.pdffX  rbX  /Ѥ         data/extensions/apcu.pdff2  rb2   '         data/extensions/psr.pdff]v rb]v Ž         data/extensions/wddx.pdff  rb           data/extensions/msgpack.pdff   rb   D5      %   data/extensions/phpdbg_webhelper.pdff~   rb~   HBC         data/extensions/decimal.pdffF  rbF  _         data/extensions/gearman.pdff rb  aѤ         data/extensions/igbinary.pdff  rb  DwZ
         data/extensions/amqp.pdff՞ rb՞ V         data/extensions/zip.pdff  rb  )rW         data/extensions/yaml.pdff  rb  ;;,         data/extensions/ast.pdff[  rb[  "Ȥ         data/extensions/bitset.pdff  rb  Ф         data/extensions/xdebug.pdff]=  rb]=  pg         data/extensions/teds.pdff rb          data/extensions/uuid.pdff  rb  j1         data/extensions/xmlrpc.pdff&!  rb&!  N         data/extensions/inotify.pdff  rb  pAm         data/extensions/redis.pdff0 rb0          data/extensions/memcached.pdfft  rbt  K|         data/ibase.inif  rbf  $         data/judy.ini   rb   S         data/fdf.ini  rb  ܤ         data/mongo.ini@  rb@  1?(         data/spl.ini@  rb@  i۬         data/rar.iniG  rbG  /         data/php_logins.jsone  rbe  wr         data/yis.ini,  rb,  B         data/phpcsfixer.json  rb  CS]         data/kdm5.ini  rb  Q         data/xml.inih  rbh  ֤         data/reflection.ini  rb  Ƣ         data/pcov.ini$  rb$  U         data/http.ini  rb  oP         data/directives.ini?t  rb?t  8b9         data/special_ip.ini   rb    Ѥ         data/readline.iniN  rbN  ^+         data/curl.iniA[  rbA[  ~j=         data/throw_exceptions.json  rb  Ut         data/php_attributes.json   rb   &\         data/seaslog.ini3  rb3  Hڞ         data/trader.ini@  rb@  k         data/phalcon.ini3  rb3  l         data/resource_usage.json  rb  a         data/tokyotyrant.ini   rb   L̤         data/debug.inia  rba  eѠ         data/intl.ini)  rb)  z¤         data/php_superglobals.ini   rb   $         data/grpc.ini  rb  u]a         data/session.ini  rb  W)Ĥ         data/wincache.ini  rb  }         data/gd.ini&  rb&           data/NoDirectUsage.ini   rb   [g         data/tidy.ini  rb  z$ߤ         data/file.inir  rbr  bin         data/ereg.ini   rb   QK         data/rdkafka.ini  rb  ~uM         data/incoming_data.json   rb   C         data/hrtime.ini   rb            data/SpecialIntegers.ini	  rb	  dw         data/wddx.ini  rb  W^M         data/ffi.ini   rb            data/pspell.ini  rb  V         data/async.ini  rb  YN         data/mbstring.ini  rb  N         data/sdl.ini  rb  Xi         data/libevent.ini  rb  
         data/uuid.iniM  rbM  
          data/php_constants.iniEb  rbEb           data/ming.ini<  rb<  v         data/suhosin.ini:  rb:  <ǌ         data/apcu.ini  rb           data/sockets.ini.  rb.  Ws¤         data/array.ini  rb  M         data/cmark.ini  rb           data/xmlrpc.inir  rbr  f         data/varnish.inir  rbr  pM         data/newt.inin  rbn  H#         data/mcrypt.inib  rbb  3g         data/psr/psr-11.json  rb           data/psr/psr-16.json	  rb	  ݿǤ         data/psr/psr-7.json^[  rb^[  m,         data/psr/psr-6.json  rb  *F         data/psr/psr-3.json~  rb~  EL         data/psr/psr-13.json  rb  .o         data/igbinary.ini  rb  p*         data/amqp.ini  rb  <ݤ         data/bcmath.ini.  rb.  Bj7         data/function_to_oop.ini	R  rb	R  qM         data/mail.ini   rb   2         data/bzip2.iniB  rbB  m7         data/constant_usage.ini  rb  $F?         data/php_reserved_types.ini   rb   E7         data/mailer.ini   rb            data/http_headers.ini  rb  I         data/ncurses.ini>$  rb>$  ݤ         data/xdiff.ini  rb  n1v         data/db2.ini  rb  هX
         data/zip.ini  rb  Ф         data/php_with_callback.ini	  rb	  V         data/libxml.ini~  rb~  w         data/php_exception.ini  rb  F         data/php_sapi.ini  rb  2p         data/memcache.ini$  rb$  7V         data/json.ini  rb  }ƕ         data/geoip.iniX  rbX  <         data/php_safe.ini   rb   wE         data/password_keys.json5  rb5  P         data/svm.ini   rb   n~q         data/standard.iniH  rbH  rߤ         data/dba.ini  rb  &s         data/phar.ini   rb   K         data/nsapi.ini   rb   6U,q         data/hash.ini  rb  ],         data/opencensus.inih  rbh  g         data/php_manual_values.json  rb  ö3P         data/analyzers.sqlite  rb           data/spip/_dist.iniH  rbH  N         data/HttpStatus.ini  rb  C         data/lua.inij  rbj  X6         data/htmlentities_constants.ini\  rb\  L         data/serviceConfig.json#  rb#  O         data/mhash.ini  rb  (Ž         data/pcntl.iniC  rbC  nM8         data/parsekit.ini  rb  \         data/zendf3.sqlite  rb  7         data/php_internet_domains.ini  rb  i         data/pdo.ini   rb   -I         data/displayFunctions.ini   rb   %         data/php_handlers.ini   rb   w&0         data/info.ini  rb  vޣ         data/ssh2.ini  rb  /i۪          data/php_constant_arguments.jsonG$  rbG$  .kk         data/crypto.ini  rb  o4         data/opcache.ini@  rb@  lu         data/oci8.ini  rb  k         data/expect.ini.  rb.  zܤ         data/apc.ini  rb  <         data/leveldb.ini  rb           data/cakephp.sqlite   rb            data/psr.ini  rb  s         data/libraries.sqlite    rb    %9         data/cyrus.ini  rb  f<ۤ         data/php_magic_methods.ini  rb  Q         data/gmagick.ini   rb   c         data/mailparse.ini  rb  :j         data/fam.iniJ  rbJ  z'         data/event.ini  rb  5pj         data/php_directives.ini#  rb#  hǤ         data/xsl.ini  rb  sG         human/.DS_Store   rb   NY&]      *   human/en/Performances/MemoizeMagicCall.inin  rbn  ,]X      '   human/en/Performances/UseArraySlice.ini   rb   !'      &   human/en/Performances/SimpleSwitch.iniJ  rbJ  .      )   human/en/Performances/SimplifyForeach.ini]  rb]  =|      )   human/en/Performances/timeVsstrtotime.ini$  rb$  :      %   human/en/Performances/SubstrFirst.ini  rb        )   human/en/Performances/DoubleArrayFlip.ini  rb  V|      2   human/en/Performances/CacheVariableOutsideLoop.inii  rbi  A      "   human/en/Performances/DoInBase.iniT  rbT  +:q      %   human/en/Performances/UseBlindVar.ini  rb  `      "   human/en/Performances/JoinFile.ini  rb  dyA      3   human/en/Performances/StaticCallDontNeedObjects.ini  rb  Aݤ      +   human/en/Performances/FetchOneRowFormat.iniI  rbI  L      '   human/en/Performances/ClassOperator.iniv  rbv  .A      /   human/en/Performances/ArrayKeyExistsSpeedup.iniB  rbB  dr      &   human/en/Performances/NotCountNull.ini	  rb	  -W      $   human/en/Performances/CsvInLoops.ini  rb  ߌm      '   human/en/Performances/SlowFunctions.ini  rb        )   human/en/Performances/OptimizeExplode.inig  rbg  ə      -   human/en/Performances/PHP7EncapsedStrings.iniD  rbD  9ɤ      *   human/en/Performances/PrePostIncrement.inih  rbh  T      *   human/en/Performances/RegexOnCollector.ini  rb  Sቤ          human/en/Performances/NoGlob.ini>  rb>  C, f      -   human/en/Performances/Php74ArrayKeyExists.iniG  rbG  MѤ      '   human/en/Performances/RegexOnArrays.ini  rb  F4      *   human/en/Performances/LogicalToInArray.ini(  rb(  hXĤ      '   human/en/Performances/StrposTooMuch.iniB  rbB  m      (   human/en/Performances/MbStringInLoop.ini  rb        +   human/en/Performances/ArrayMergeInLoops.ini,	  rb,	  CT      (   human/en/Performances/NoConcatInLoop.ini]	  rb]	        )   human/en/Performances/IssetWholeArray.ini  rb  Tw      $   human/en/Performances/Autoappend.ini;  rb;  [      %   human/en/Performances/MakeOneCall.ini  rb        (   human/en/Performances/AvoidArrayPush.iniI  rbI  NX@ͤ         human/en/Vendors/Laravel.ini  rb  |h         human/en/Vendors/Sylius.ini?  rb?  TV3          human/en/Vendors/Codeigniter.iniE  rbE  \9         human/en/Vendors/Typo3.iniO  rbO  O*٤         human/en/Vendors/Wordpress.ini  rb  I         human/en/Vendors/Yii.iniJ  rbJ  d         human/en/Vendors/Drupal.ini  rb  h         human/en/Vendors/Phalcon.ini  rb  ,b         human/en/Vendors/Joomla.iniq  rbq  U         human/en/Vendors/Fuel.ini*  rb*  Ͳ         human/en/Vendors/Ez.iniD  rbD           human/en/Vendors/Concrete5.ini!  rb!  |G         human/en/Vendors/Symfony.ini"  rb"  ܸ      %   human/en/Dump/DereferencingLevels.ini3  rb3  ]       '   human/en/Dump/CollectPropertyCounts.ini+  rb+  !F      2   human/en/Dump/CollectNativeCallsPerExpressions.ini  rb  A      "   human/en/Dump/CollectBlockSize.inio  rbo  .o      '   human/en/Dump/CollectParameterNames.ini  rb  j3Ĥ      )   human/en/Dump/ParameterArgumentsLinks.ini  rb  ^d       "   human/en/Dump/TypehintingStats.ini  rb  ѕg      (   human/en/Dump/CollectParameterCounts.ini  rb  h(ݤ         human/en/Dump/PublicReach.inip  rbp  dɅ         human/en/Dump/CallOrder.ini,  rb,  a.C      $   human/en/Dump/CollectReadability.ini|  rb|  sgd      &   human/en/Dump/CyclomaticComplexity.ini  rb  4      &   human/en/Dump/CollectPhpStructures.iniK  rbK  5G      "   human/en/Dump/CouldBeAConstant.ini  rb  ~nt      (   human/en/Dump/EnvironnementVariables.ini  rb  _}      "   human/en/Dump/CollectUseCounts.iniV  rbV  D"      #   human/en/Dump/CollectClassDepth.ini  rb   Rz      -   human/en/Dump/CollectClassInterfaceCounts.ini&  rb&  Ԥ      (   human/en/Dump/CollectForeachFavorite.ini  rb        %   human/en/Dump/CollectClassChanges.inib  rbb  B-      '   human/en/Dump/CollectStubStructures.ini  rb  ]      ,   human/en/Dump/CollectClassConstantCounts.ini  rb  j߫      *   human/en/Dump/CollectMbstringEncodings.ini#  rb#  <ﬤ      !   human/en/Dump/CollectLiterals.ini  rb  i(vm         human/en/Dump/Inclusions.ini  rb  ?         human/en/Dump/Typehintorder.ini  rb  \      *   human/en/Dump/CollectClassTraitsCounts.iniO  rbO  h      "   human/en/Dump/CollectVariables.ini5  rb5  0         human/en/Dump/NewOrder.ini  rb        )   human/en/Dump/CollectDefinitionsStats.ini  rb  $      ,   human/en/Dump/CollectDependencyExtension.ini  rb  lɓ         human/en/Dump/ConstantOrder.ini  rb  E=[       ,   human/en/Dump/CollectLocalVariableCounts.ini  rb  2_      #   human/en/Dump/IndentationLevels.ini  rb  V`ä      &   human/en/Dump/EnvironmentVariables.ini  rb  S      *   human/en/Dump/CollectFilesDependencies.iniv  rbv  Rzx      ,   human/en/Dump/CollectClassesDependencies.iniW  rbW  Z      %   human/en/Dump/CollectMethodCounts.iniI  rbI  Τ      #   human/en/Dump/FossilizedMethods.iniK  rbK  3      &   human/en/Dump/CollectClassChildren.iniP  rbP  ;Q      #   human/en/Dump/CollectAtomCounts.ini2  rb2  +<      (   human/en/Dump/CollectGlobalVariables.inij  rbj  <ˤ         human/en/.DS_Storep  rbp   j      "   human/en/Patterns/AbstractAway.ini?	  rb?	  ]<      )   human/en/Patterns/CourrierAntiPattern.ini  rb  ^/      "   human/en/Patterns/GetterSetter.ini  rb  T      )   human/en/Patterns/DependencyInjection.ini  rb  I         human/en/Patterns/Factory.ini  rb  T          human/en/Structures/NotEqual.ini3  rb3  Xj      -   human/en/Structures/ArrayMergeAndVariadic.ini  rb  H      $   human/en/Structures/UnusedGlobal.ini  rb        1   human/en/Structures/NoChangeIncomingVariables.ini  rb  -      +   human/en/Structures/TimestampDifference.ini  rb  j      '   human/en/Structures/IdenticalElseif.ini  rb  "	      %   human/en/Structures/RepeatedRegex.ini  rb  iM      %   human/en/Structures/StrposCompare.ini  rb  |er      "   human/en/Structures/WrongRange.iniv  rbv  x	      &   human/en/Structures/EchoWithConcat.ini  rb  ,5      +   human/en/Structures/UselessNullCoalesce.ini  rb  Bس      ,   human/en/Structures/SwitchWithoutDefault.inip  rbp  !e      )   human/en/Structures/DeclareStaticOnce.ini  rb  K      .   human/en/Structures/ArrayMergeWithEllipsis.ini  rb  (      !   human/en/Structures/LoneBlock.ini
  rb
  `Ȣ      +   human/en/Structures/MultipleDefinedCase.ini  rb  @      "   human/en/Structures/SimplePreg.ini  rb  D=k      !   human/en/Structures/FileUsage.ini  rb  ױ      &   human/en/Structures/NoNeedGetClass.ini  rb  yT      ,   human/en/Structures/UseUrlQueryFunctions.ini%  rb%  y      )   human/en/Structures/IssetWithConstant.ini  rb        &   human/en/Structures/UselessCasting.iniF	  rbF	   HF      #   human/en/Structures/UnusedLabel.ini  rb  _	t      /   human/en/Structures/PrintWithoutParenthesis.ini  rb  B      #   human/en/Structures/NegativePow.ini  rb  H9      #   human/en/Structures/UseConstant.ini  rb        '   human/en/Structures/BreakNonInteger.ini~  rb~  iC豤      *   human/en/Structures/ComparedComparison.ini*  rb*  yP         human/en/Structures/AddZero.ini  rb  +       (   human/en/Structures/ImplodeArgsOrder.ini  rb        %   human/en/Structures/NestedTernary.iniR  rbR  !P      !   human/en/Structures/ExitUsage.ini	  rb	  ڋ      -   human/en/Structures/UseVariableInsideLoop.iniT  rbT  dM      &   human/en/Structures/GlobalInGlobal.iniy  rby  Fɤ      %   human/en/Structures/MultipleCatch.inij  rbj  wGt      0   human/en/Structures/CouldUseNullableOperator.inim	  rbm	  c㥤      %   human/en/Structures/MultipleUnset.ini  rb  mä      %   human/en/Structures/CastToBoolean.ini7  rb7  UDv      /   human/en/Structures/ArraySearchMultipleKeys.ini|  rb|  @#      !   human/en/Structures/ElseUsage.ini  rb        #   human/en/Structures/ModernEmpty.ini  rb  f      #   human/en/Structures/Fallthrough.ini  rb  طޤ      )   human/en/Structures/UseArrayFunctions.ini  rb  8      &   human/en/Structures/SwitchToSwitch.ini	  rb	  ζP      (   human/en/Structures/CouldUseArraySum.iniO  rbO  .J/      '   human/en/Structures/DontBeTooManual.iniw  rbw  CM      #   human/en/Structures/CouldBeElse.ini<	  rb<	  I.      /   human/en/Structures/BooleanStrictComparison.ini;  rb;  s¬      3   human/en/Structures/McryptcreateivWithoutOption.ini  rb        &   human/en/Structures/VariableGlobal.ini  rb  k>      +   human/en/Structures/ConstDefineFavorite.inil  rbl  4      #   human/en/Structures/NoSubstrOne.inib  rbb        "   human/en/Structures/NamedRegex.ini	  rb	  ;      (   human/en/Structures/CouldBeSpaceship.ini  rb  Km      1   human/en/Structures/InconsistentConcatenation.ini  rb  g.5      #   human/en/Structures/ConcatEmpty.inib  rbb  /xs@      )   human/en/Structures/InvalidPackFormat.ini.  rb.  '      *   human/en/Structures/DontChangeBlindKey.ini  rb  "gC      *   human/en/Structures/UseListWithForeach.ini
  rb
  i[w      ,   human/en/Structures/UsePositiveCondition.ini&  rb&  >H      $   human/en/Structures/IncludeUsage.ini  rb  uSE      *   human/en/Structures/UncheckedResources.ini=  rb=  Q>      +   human/en/Structures/ForWithFunctioncall.iniY  rbY  b      '   human/en/Structures/DirectlyUseFile.inir  rbr  =o      &   human/en/Structures/ResourcesUsage.iniq  rbq        ,   human/en/Structures/CatchShadowsVariable.ini5  rb5  X      $   human/en/Structures/UselessUnset.ini  rb  ǩͤ      .   human/en/Structures/DontReuseForeachSource.ini  rb  ? 2      $   human/en/Structures/InvalidRegex.ini  rb  R;      )   human/en/Structures/UseCountRecursive.ini  rb  jeBΤ      *   human/en/Structures/ResultMayBeMissing.ini  rb  W_w      #   human/en/Structures/NestedLoops.ini  rb  [      %   human/en/Structures/ListOmissions.ini|  rb|  ⵤ      !   human/en/Structures/ImpliedIf.inii  rbi  EM      '   human/en/Structures/FileUploadUsage.ini  rb  k6      #   human/en/Structures/GlobalUsage.ini:  rb:  ˊ      %   human/en/Structures/MultiplyByOne.ini  rb        5   human/en/Structures/OneDotOrObjectOperatorPerLine.inix  rbx  =g      (   human/en/Structures/AutoUnsetForeach.ini#  rb#  ;𶀤      7   human/en/Structures/DontReadAndWriteInOneExpression.ini  rb  )&r      )   human/en/Structures/UnknownPregOption.ini  rb  'S      )   human/en/Structures/ContinueIsForLoop.ini
  rb
  =Ť      %   human/en/Structures/ReuseVariable.ini  rb  U,/!      $   human/en/Structures/DirThenSlash.ini  rb  R          human/en/Structures/UseDebug.ini  rb  \      $   human/en/Structures/UseCaseValue.ini_  rb_  KըV      )   human/en/Structures/AssigneAndCompare.ini  rb  ^[      %   human/en/Structures/UselessGlobal.ini  rb  <      1   human/en/Structures/SwitchWithMultipleDefault.ini  rb  QG      %   human/en/Structures/CheckDivision.ini	  rb	  y9M      0   human/en/Structures/ConstantScalarExpression.ini,  rb,  o~r+      &   human/en/Structures/GtOrLtFavorite.ini  rb  뷤      &   human/en/Structures/UnsetInForeach.ini  rb  /      '   human/en/Structures/DereferencingAS.ini  rb  2       ,   human/en/Structures/EchoPrintConsistance.ini$  rb$  ͞`      %   human/en/Structures/NextMonthTrap.ini~	  rb~	  /L      (   human/en/Structures/RandomWithoutTry.ini  rb  vL      %   human/en/Structures/ShouldUseMath.ini  rb  f      ,   human/en/Structures/ArrayFillWithObjects.ini  rb  gȏ      &   human/en/Structures/CouldBeTernary.ini  rb  \x-      0   human/en/Structures/NonBreakableSpaceInNames.ini  rb  n=z      '   human/en/Structures/ReturnTrueFalse.ini  rb  h	      $   human/en/Structures/TestThenCast.ini  rb  %      ,   human/en/Structures/ArrayMergeArrayArray.iniK  rbK  CBФ          human/en/Structures/NoChoice.ini  rb  4r      ,   human/en/Structures/UnconditionLoopBreak.ini
  rb
  Q      .   human/en/Structures/VariableMayBeNonGlobal.ini  rb  㺎      &   human/en/Structures/BasenameSuffix.inio  rbo  ~1      &   human/en/Structures/CastingTernary.ini  rb  .W          human/en/Structures/SetAside.ini  rb  3}2      !   human/en/Structures/EvalUsage.ini	  rb	        3   human/en/Structures/IdenticalVariablesInForeach.ini  rb  n;      !   human/en/Structures/CheckJson.ini  rb  '      ,   human/en/Structures/FunctionSubscripting.iniN  rbN  ę      -   human/en/Structures/IndicesAreIntOrString.ini  rb  b      *   human/en/Structures/ForeachSourceValue.inii	  rbi	  Gd      )   human/en/Structures/MismatchedTernary.iniK  rbK  j      +   human/en/Structures/AssignedInOneBranch.ini  rb  $      %   human/en/Structures/CouldUseMatch.ini'  rb'  GS      -   human/en/Structures/OverwrittenForeachVar.iniH  rbH        "   human/en/Structures/TryFinally.ini  rb  l'      $   human/en/Structures/DynamicCalls.ini:  rb:        ,   human/en/Structures/IdenticalConsecutive.iniX  rbX  VW      %   human/en/Structures/QueriesInLoop.iniQ  rbQ  ♅      '   human/en/Structures/LogicalMistakes.ini/  rb/  6'Ϥ      %   human/en/Structures/SubstrLastArg.ini  rb  <Uְ         human/en/Structures/NotNot.ini  rb  Y      "   human/en/Structures/ShellUsage.ini  rb  <%      &   human/en/Structures/NoDirectAccess.ini   rb   \'      $   human/en/Structures/UselessCheck.ini  rb  a$      7   human/en/Structures/OnlyVariableReturnedByReference.iniJ  rbJ        3   human/en/Structures/HtmlentitiescallDefaultFlag.ini^  rb^  oz      %   human/en/Structures/TooManyElseif.ini  rb  /      *   human/en/Structures/ComparisonFavorite.ini  rb  6>      %   human/en/Structures/UseInstanceof.ini  rb  Τ      '   human/en/Structures/UselessBrackets.ini  rb  j      !   human/en/Structures/ShortTags.iniw  rbw  E>      $   human/en/Structures/MissingCases.ini  rb  3j      (   human/en/Structures/Htmlentitiescall.inin  rbn  -)      )   human/en/Structures/PossibleIncrement.ini  rb  P      &   human/en/Structures/NoGetClassNull.ini  rb  x      ,   human/en/Structures/IdenticalOnBothSides.ini(  rb(  E=扤      (   human/en/Structures/NoIssetWithEmpty.ini>  rb>  &^      ,   human/en/Structures/PossibleInfiniteLoop.ini  rb  Q      %   human/en/Structures/NoHardcodedIp.ini
  rb
        $   human/en/Structures/ElseIfElseif.ini  rb  i'S      5   human/en/Structures/ForeachReferenceIsNotModified.ini  rb  If      0   human/en/Structures/MixedConcatInterpolation.inii  rbi        )   human/en/Structures/GlobalOutsideLoop.iniy  rby  F      %   human/en/Structures/EmptyTryCatch.ini  rb  7m      1   human/en/Structures/ErrorReportingWithInteger.ini<  rb<  Wl      +   human/en/Structures/IdenticalConditions.ini  rb        %   human/en/Structures/UseFileAppend.ini	  rb	  g      -   human/en/Structures/CouldUseArrayFillKeys.ini  rb  ap      '   human/en/Structures/NoHardcodedHash.ini  rb  dt\      (   human/en/Structures/MbstringThirdArg.ini
  rb
  @      /   human/en/Structures/CalltimePassByReference.inic  rbc  [(h      5   human/en/Structures/UnsupportedTypesWithOperators.iniH  rbH  J      :   human/en/Structures/ConcatenationInterpolationFavorite.ini  rb  b      )   human/en/Structures/DoubleInstruction.iniH  rbH  N]      #   human/en/Structures/PHP7Dirname.ini'  rb'  2|      (   human/en/Structures/NoAppendOnSource.ini  rb  >J      %   human/en/Structures/CheckAllTypes.ini  rb  \G      '   human/en/Structures/GoToKeyDirectly.iniX  rbX  p(      #   human/en/Structures/Iffectation.ini,  rb,  X$Ȥ      %   human/en/Structures/ErrorMessages.ini$  rb$  EHgU      &   human/en/Structures/CurlVersionNow.ini  rb  VE&S      8   human/en/Structures/OpensslRandomPseudoByteSecondArg.iniF  rbF  (Ȥ      '   human/en/Structures/NoHardcodedPort.ini  rb  a      &   human/en/Structures/DontAddSeconds.ini  rb  .%P      +   human/en/Structures/DropElseAfterReturn.ini  rb  
~      !   human/en/Structures/LongBlock.inip  rbp  j      -   human/en/Structures/ConditionalStructures.ini  rb  |4+      %   human/en/Structures/NoDirectUsage.iniA  rbA  @cw      )   human/en/Structures/BuriedAssignation.iniE  rbE  9;      %   human/en/Structures/NeverNegative.ini  rb  1z      !   human/en/Structures/MailUsage.ini  rb  >       %   human/en/Structures/LongArguments.inid  rbd  \      0   human/en/Structures/HeredocDelimiterFavorite.inib  rbb  yx      (   human/en/Structures/CryptWithoutSalt.ini  rb  <i      )   human/en/Structures/DoubleAssignation.ini}  rb}  Ny_!      #   human/en/Structures/PrintAndDie.iniU  rbU  5*      /   human/en/Structures/StripTagsSkipsClosedTag.iniH  rbH  #D      %   human/en/Structures/OnlyFirstByte.ini  rb        %   human/en/Structures/RepeatedPrint.ini=  rb=  %3      &   human/en/Structures/EvalWithoutTry.ini  rb  [#ʤ      -   human/en/Structures/ArrayMapPassesByValue.ini<  rb<  Y      *   human/en/Structures/CommonAlternatives.ini,  rb,        *   human/en/Structures/MissingParenthesis.ini  rb  Ѳ      %   human/en/Structures/SequenceInFor.ini  rb  a      "   human/en/Structures/EmptyLines.ini  rb  {I      )   human/en/Structures/ShouldMakeTernary.iniA  rbA  V#A      "   human/en/Structures/StaticLoop.ini  rb  liĤ      '   human/en/Structures/ForeachWithList.ini#  rb#  DԞ      "   human/en/Structures/MissingNew.ini  rb  y,      '   human/en/Structures/CouldUseCompact.inib  rbb  S9      &   human/en/Structures/ImplicitGlobal.ini  rb        )   human/en/Structures/OneIfIsSufficient.ini  rb  Ć      "   human/en/Structures/ReturnVoid.iniV  rbV  쨄      %   human/en/Structures/NoNeedForElse.ini  rb   
7      )   human/en/Structures/ShouldUseOperator.ini  rb  ]      )   human/en/Structures/ComplexExpression.ini  rb  ˀ7      '   human/en/Structures/ThrowsAndAssign.iniI  rbI  ӂ#      %   human/en/Structures/NoArrayUnique.ini  rb  b繤      (   human/en/Structures/ShouldPreprocess.ini  rb  U      &   human/en/Structures/YodaComparison.ini  rb  O ˤ         human/en/Structures/Break0.ini'  rb'  1y         human/en/Structures/OrDie.ini  rb  @      4   human/en/Structures/AlternativeConsistenceByFile.ini  rb  5h{      #   human/en/Structures/EmptyBlocks.ini  rb  &T      (   human/en/Structures/ShouldUseForeach.ini
  rb
  QOŤ         human/en/Structures/IsZero.ini  rb  T	b      '   human/en/Structures/UnreachableCode.ini9  rb9  B      #   human/en/Structures/AlwaysFalse.iniW  rbW        3   human/en/Structures/ForeachNeedReferencedSource.ini  rb        /   human/en/Structures/SetlocaleNeedsConstants.ini5  rb5  ̤      /   human/en/Structures/toStringThrowsException.iniw  rbw  @ɤ      )   human/en/Structures/NoReturnInFinally.ini  rb  f/      5   human/en/Structures/ConstantComparisonConsistance.ini  rb  Sf*      ,   human/en/Structures/SGVariablesConfusion.iniw  rbw  g      &   human/en/Structures/DuplicateCalls.ini  rb  ~k      '   human/en/Structures/DontMixPlusPlus.ini  rb  /}0      )   human/en/Structures/CoalesceAndConcat.iniL  rbL        8   human/en/Structures/OneExpressionBracketsConsistency.ini  rb  Dh      %   human/en/Structures/CouldBeStatic.ini
  rb
  Τ      /   human/en/Structures/DontCompareTypedBoolean.inin  rbn  DX      #   human/en/Structures/CouldUseDir.ini  rb  <?      3   human/en/Structures/StringInterpolationFavorite.ini  rb  .9          human/en/Structures/NotOrNot.iniE  rbE  $      .   human/en/Structures/NoVariableIsACondition.inib  rbb  U       7   human/en/Structures/AlteringForeachWithoutReference.ini  rb  ڤ      &   human/en/Structures/JsonWithOption.iniW  rbW  'j(      *   human/en/Structures/UselessParenthesis.iniT  rbT  яͤ      '   human/en/Structures/DontLoopOnYield.ini=  rb=  S      ,   human/en/Structures/IfWithSameConditions.ini	  rb	  Fg      1   human/en/Structures/PropertyVariableConfusion.ini  rb  l9      $   human/en/Structures/PhpinfoUsage.ini  rb  6c      /   human/en/Structures/UnsupportedOperandTypes.inim  rbm  7ㅤ      $   human/en/Structures/NestedIfthen.ini  rb  t      ,   human/en/Structures/ShouldUseExplodeArgs.ini,  rb,  Τ      -   human/en/Structures/OneLevelOfIndentation.ini  rb        /   human/en/Structures/NoAssignationInFunction.ini  rb  ͤ      $   human/en/Structures/NewLineStyle.ini  rb  O㑈      (   human/en/Structures/BreakOutsideLoop.ini  rb  ^Ϥ          human/en/Structures/Noscream.iniZ  rbZ  ,u      ,   human/en/Structures/MultipleSimilarCalls.ini		  rb		  9م      #   human/en/Structures/pregOptionE.ini1  rb1  tn      &   human/en/Structures/RegexDelimiter.ini  rb  cʢb      #   human/en/Structures/MergeIfThen.ini  rb  $      /   human/en/Structures/FunctionPreSubscripting.iniQ  rbQ  (¡      '   human/en/Structures/TernaryInConcat.ini{  rb{  *b      &   human/en/Structures/Unpreprocessed.ini   rb   9      /   human/en/Structures/MbstringUnknownEncoding.ini  rb  rԤ      )   human/en/Structures/CouldUseStrrepeat.ini6  rb6  v      #   human/en/Structures/DynamicCode.ini  rb  M`ƞ      !   human/en/Structures/OnceUsage.ini  rb        $   human/en/Structures/NoEmptyRegex.ini[  rb[  }!      $   human/en/Structures/BailOutEarly.ini  rb  F\      )   human/en/Structures/InfiniteRecursion.ini  rb  0i      ,   human/en/Structures/SuspiciousComparison.ini  rb  Q?      ,   human/en/Structures/CanCountNonCountable.ini  rb  {ã      +   human/en/Structures/CouldUseArrayUnique.ini
  rb
  c+E      &   human/en/Structures/SameConditions.ini1  rb1  Yd      *   human/en/Structures/ConstantConditions.iniR  rbR  #ڤ      +   human/en/Structures/EmptyWithExpression.ini  rb  #U      '   human/en/Structures/NoObjectAsIndex.ini  rb  FΤ      (   human/en/Structures/ObjectReferences.iniY  rbY  ($      $   human/en/Structures/SubstrToTrim.inif  rbf  /?      ,   human/en/Structures/MultipleTypeVariable.ini  rb  :      +   human/en/Structures/ForgottenWhiteSpace.inii  rbi  ~ ٲ      '   human/en/Structures/PrintfArguments.ini2  rb2  ϥ      .   human/en/Structures/OneLineTwoInstructions.ini	  rb	  %      #   human/en/Structures/Bracketless.inif  rbf  8      9   human/en/Structures/NoParenthesisForLanguageConstruct.ini"  rb"        /   human/en/Structures/DanglingArrayReferences.iniC  rbC  S5      '   human/en/Structures/NoHardcodedPath.ini
  rb
  SLf      *   human/en/Structures/InconsistentElseif.iniG  rbG         0   human/en/Structures/CouldUseShortAssignation.inik  rbk  ]8m      *   human/en/Structures/UselessInstruction.ini"  rb"  %]&      ,   human/en/Structures/DifferencePreference.ini  rb  ;Dɤ      $   human/en/Structures/UseSystemTmp.ini  rb        $   human/en/Structures/VardumpUsage.ini	  rb	  T5Ĥ      5   human/en/Structures/ComparedButNotAssignedStrings.ini>  rb>  Ϊ      #   human/en/Structures/PlusEgalOne.ini  rb  |pӤ      /   human/en/Structures/FailingSubstrComparison.ini  rb  >      ,   human/en/Structures/MaxLevelOfIdentation.ini  rb  J?c      ,   human/en/Structures/ShouldChainException.ini	  rb	  f?      )   human/en/Structures/NoReferenceOnLeft.ini  rb  TӤ      *   human/en/Structures/DieExitConsistance.ini  rb  ~k      %   human/en/Structures/UselessSwitch.iniK  rbK        %   human/en/Structures/WhileListEach.ini	  rb	  CJ      /   human/en/Structures/DoubleObjectAssignation.ini  rb  h      '   human/en/Structures/NoNeedForTriple.ini  rb  z      )   human/en/Traits/CannotCallTraitMethod.ini  rb  4      !   human/en/Traits/CouldUseTrait.ini  rb  Ί         human/en/Traits/Php.inik  rbk  	\-         human/en/Traits/EmptyTrait.ini  rb  ]ڤ         human/en/Traits/IsExtTrait.ini6  rb6  nQ         human/en/Traits/UsedTrait.ini  rb  iN      "   human/en/Traits/SelfUsingTrait.inik  rbk  6Pۤ         human/en/Traits/TraitUsage.ini  rb  'nP      '   human/en/Traits/AlreadyParentsTrait.ini  rb  
v      "   human/en/Traits/DependantTrait.ini  rb  U      )   human/en/Traits/MethodCollisionTraits.ini  rb  =      !   human/en/Traits/TraitNotFound.ini  rb  <:!A         human/en/Traits/Traitnames.ini  rb  m7a          human/en/Traits/UselessAlias.ini/  rb/  "6      !   human/en/Traits/MultipleUsage.ini  rb  $         human/en/Traits/UnusedTrait.ini`  rb`           human/en/Traits/TraitMethod.ini  rb  k      $   human/en/Traits/UnusedClassTrait.ini>  rb>  ]}      '   human/en/Traits/LocallyUsedProperty.ini  rb  -¤      &   human/en/Traits/UndefinedInsteadof.ini  rb  <      "   human/en/Traits/UndefinedTrait.ini  rb  Aw      $   human/en/Complete/PropagateCalls.ini  rb  khP      @   human/en/Complete/SetClassRemoteDefinitionWithReturnTypehint.ini  rb  ޶      (   human/en/Complete/PhpNativeReference.ini  rb  BU      '   human/en/Complete/ExtendedTypehints.ini;  rb;  ,      +   human/en/Complete/OverwrittenProperties.ini}  rb}  +a      "   human/en/Complete/SetCloneLink.ini=  rb=  h      $   human/en/Complete/ReturnTypehint.ini   rb   z      -   human/en/Complete/SetClassAliasDefinition.ini  rb  d      -   human/en/Complete/SetArrayClassDefinition.ini  rb  VΤ      $   human/en/Complete/IsExtStructure.iniA  rbA  6      :   human/en/Complete/SetClassRemoteDefinitionWithTypehint.ini  rb        (   human/en/Complete/OverwrittenMethods.ini  rb        )   human/en/Complete/SetParentDefinition.ini  rb  iǤ      %   human/en/Complete/IsStubStructure.ini0  rb0  n=      (   human/en/Complete/PropagateConstants.ini  rb  /ͤ      <   human/en/Complete/SetClassPropertyDefinitionWithTypehint.ini  rb  ɔ      /   human/en/Complete/MakeClassMethodDefinition.ini  rb  OTpt      1   human/en/Complete/MakeClassConstantDefinition.ini  rb  #Ǥ      =   human/en/Complete/SetClassRemoteDefinitionWithParenthesis.ini  rb  񐵤      8   human/en/Complete/SetClassRemoteDefinitionWithGlobal.ini  rb  o      .   human/en/Complete/PhpExtStubPropertyMethod.ini  rb  ~      )   human/en/Complete/CreateDefaultValues.ini  rb  |      ,   human/en/Complete/CreateCompactVariables.ini  rb  |+$      $   human/en/Complete/IsPhpStructure.ini;  rb;  >Is      :   human/en/Complete/SetClassRemoteDefinitionWithLocalNew.ini  rb  ٍ1      3   human/en/Complete/MakeFunctioncallWithReference.ini  rb  EfK      -   human/en/Complete/FollowClosureDefinition.ini"  rb"        )   human/en/Complete/CreateMagicProperty.ini  rb  7      '   human/en/Complete/SolveTraitMethods.ini  rb  `r      '   human/en/Complete/CreateMagicMethod.ini  rb  |M      ;   human/en/Complete/SetClassRemoteDefinitionWithInjection.inir  rbr  tO      4   human/en/Complete/SetClassMethodRemoteDefinition.ini  rb  ~      *   human/en/Complete/CreateForeachDefault.ini  rb  o9      /   human/en/Complete/SetStringMethodDefinition.ini9  rb9  {(      *   human/en/Complete/OverwrittenConstants.ini7  rb7  1      &   human/en/Complete/VariableTypehint.ini  rb  Ť      &   human/en/Security/CantDisableClass.iniN  rbN  `ˤ      %   human/en/Security/MinusOneOnError.ini  rb  tvU      2   human/en/Security/ShouldUseSessionRegenerateId.ini&  rb&  @ؤ      *   human/en/Security/SuperGlobalContagion.ini  rb  ޤ      $   human/en/Security/EncodedLetters.ini  rb  b]          human/en/Security/GPRAliases.ini  rb  +$D      !   human/en/Security/NoEntIgnore.ini  rb  w      %   human/en/Security/NoNetForXmlLoad.ini  rb  a      #   human/en/Security/DontEchoError.ini  rb  mʤ      '   human/en/Security/SensitiveArgument.ini  rb  T      !   human/en/Security/AnchorRegex.iniw  rbw  ^      !   human/en/Security/CurlOptions.ini  rb  өp      !   human/en/Security/CompareHash.ini	  rb	  O      &   human/en/Security/ConfigureExtract.ini  rb  $/Q      &   human/en/Security/AvoidThoseCrypto.ini  rb  pbҤ      &   human/en/Security/MoveUploadedFile.ini&  rb&  !VJ      )   human/en/Security/CantDisableFunction.ini  rb  #      /   human/en/Security/parseUrlWithoutParameters.ini#  rb#  ,HO      -   human/en/Security/UploadFilenameInjection.ini  rb  zE      "   human/en/Security/MkdirDefault.iniY  rbY        %   human/en/Security/RegisterGlobals.ini	  rb	  a;=      &   human/en/Security/SessionLazyWrite.iniT  rbT  ٝ      0   human/en/Security/ShouldUsePreparedStatement.inii  rbi  /      )   human/en/Security/KeepFilesRestricted.ini  rb  a         human/en/Security/DynamicDl.inia  rba  t'      %   human/en/Security/DirectInjection.ini=  rb=  tӤ      %   human/en/Security/NoWeakSSLCrypto.ini}  rb}        #   human/en/Security/SetCookieArgs.ini
  rb
  ?      '   human/en/Security/IndirectInjection.ini  rb  V      %   human/en/Security/SafeHttpHeaders.iniu  rbu  ǥ      '   human/en/Security/FilterInputSource.ini  rb  fp3      1   human/en/Security/Sqlite3RequiresSingleQuotes.iniA  rbA  9E      '   human/en/Security/IntegerConversion.iniT  rbT  4{      %   human/en/Security/CryptoKeyLength.ini  rb  u d;         human/en/Security/NoSleep.ini  rb  @J      *   human/en/Security/UnserializeSecondArg.ini	  rb	  =/      )   human/en/Classes/CantInstantiateClass.ini:  rb:  4MF         human/en/Classes/DemeterLaw.iniM  rbM  Q      #   human/en/Classes/AbstractStatic.ini  rb  N6      (   human/en/Classes/PPPDeclarationStyle.ini  rb  T-      +   human/en/Classes/CouldBeProtectedMethod.ini@  rb@  Uf      %   human/en/Classes/FossilizedMethod.ini  rb  *      &   human/en/Classes/UndefinedParentMP.ini  rb  gH|      (   human/en/Classes/UnreachableConstant.ini  rb  Q      &   human/en/Classes/RaisedAccessLevel.ini  rb  7+      '   human/en/Classes/PropertyDefinition.ini  rb        !   human/en/Classes/DynamicClass.ini'  rb'  tֻ      #   human/en/Classes/HiddenNullable.ini^  rb^  5=r      $   human/en/Classes/UselessAbstract.ini   rb   (֤      +   human/en/Classes/UnusedProtectedMethods.ini3  rb3  ŉ      $   human/en/Classes/CantExtendFinal.ini1  rb1  "      #   human/en/Classes/ClassOverreach.ini  rb  iQ      %   human/en/Classes/RedefinedDefault.iniy  rby  V%55      "   human/en/Classes/ShouldUseThis.ini  rb  8         human/en/Classes/TestClass.ini   rb   Xv      &   human/en/Classes/PropertyNeverUsed.ini  rb  eC      '   human/en/Classes/HasFluentInterface.ini  rb  ;?      ,   human/en/Classes/ChecksPropertyExistence.ini+
  rb+
  @uW      %   human/en/Classes/UsedOnceProperty.ini  rb  	<      (   human/en/Classes/ChildRemoveTypehint.ini  rb  U$潤      +   human/en/Classes/PropertyUsedInternally.ini!  rb!  U>ڤ      *   human/en/Classes/UnitializedProperties.ini  rb  8v          human/en/Classes/Finalmethod.ini  rb  )$W      $   human/en/Classes/CouldBeIterable.ini  rb  4      %   human/en/Classes/IdenticalMethods.ini(  rb(   GKl          human/en/Classes/CitSameName.iniN	  rbN	  Ox@          human/en/Classes/OldStyleVar.ini  rb        (   human/en/Classes/DisconnectedClasses.ini  rb  /      %   human/en/Classes/RedefinedMethods.ini  rb  ?a      $   human/en/Classes/DefinedStaticMP.ini  rb  5W7      &   human/en/Classes/MakeMagicConcrete.ini  rb  H      '   human/en/Classes/NonNullableSetters.ini  rb  z      %   human/en/Classes/CheckOnCallUsage.iniQ  rbQ  *         human/en/Classes/WrongName.inid  rbd  (P      &   human/en/Classes/AbstractConstants.iniZ	  rbZ	  E2>         human/en/Classes/NoParent.ini  rb  P      0   human/en/Classes/PropertyUsedInOneMethodOnly.ini  rb  ?jԤ      '   human/en/Classes/ImmutableSignature.iniH  rbH  Z|      2   human/en/Classes/NewOnFunctioncallOrIdentifier.inie  rbe  :r      )   human/en/Classes/CouldBePrivateMethod.iniB  rbB  "a      &   human/en/Classes/CouldBeStringable.ini  rb        %   human/en/Classes/ThisIsForClasses.ini  rb  ^U          human/en/Classes/UnusedClass.ini|  rb|  cs\      1   human/en/Classes/InsufficientPropertyTypehint.ini  rb  /      &   human/en/Classes/DynamicMethodCall.ini  rb  7q9          human/en/Classes/StrangeName.ini  rb  p      -   human/en/Classes/OneObjectOperatorPerLine.ini  rb  :^      "   human/en/Classes/NormalMethods.ini  rb  wlդ      %   human/en/Classes/NoMagicWithArray.ini  rb  Ac      $   human/en/Classes/AccessProtected.ini'  rb'  n      $   human/en/Classes/ExtendsStdclass.ini  rb  >      &   human/en/Classes/IntegerAsProperty.ini	  rb	  c.      .   human/en/Classes/CantInheritAbstractMethod.ini#  rb#  fP      (   human/en/Classes/UnusedPrivateMethod.ini  rb  N,ݤ      $   human/en/Classes/ClassAliasUsage.ini,  rb,  MTf      *   human/en/Classes/MultipleClassesInFile.inig  rbg  [      $   human/en/Classes/VariableClasses.ini8  rb8  ]ʤ         human/en/Classes/Classnames.ini  rb  ͂      -   human/en/Classes/UndeclaredStaticProperty.ini  rb  06      -   human/en/Classes/CouldBeProtectedProperty.ini<  rb<  ^X      %   human/en/Classes/UseClassOperator.ini  rb  A*      '   human/en/Classes/UselessConstructor.ini%  rb%  a         human/en/Classes/NonPpp.ini  rb  6^         human/en/Classes/UseThis.ini  rb  ~q=      &   human/en/Classes/UndefinedStaticMP.ini  rb  aʸ      &   human/en/Classes/AvoidOptionArrays.ini  rb  !      +   human/en/Classes/UsingThisOutsideAClass.ini  rb  .F̤         human/en/Classes/NullOnNew.ini  rb  QQ      2   human/en/Classes/StaticMethodsCalledFromObject.inio  rbo  I3Ǥ          human/en/Classes/Constructor.ini&  rb&  |Z      #   human/en/Classes/CouldBePrivate.ini  rb  @)ʤ      $   human/en/Classes/Abstractmethods.ini  rb  ե      &   human/en/Classes/TooManyInjections.ini  rb  Ԟ      $   human/en/Classes/ThrowInDestruct.ini  rb  AF[      )   human/en/Classes/TooManyDereferencing.ini  rb        ,   human/en/Classes/DirectCallToMagicMethod.ini  rb  
      '   human/en/Classes/OrderOfDeclaration.ini  rb  Iv'٤      &   human/en/Classes/UsedPrivateMethod.iniC  rbC  6      %   human/en/Classes/UninitedProperty.ini  rb  V      4   human/en/Classes/MethodSignatureMustBeCompatible.ini4  rb4  _      %   human/en/Classes/CyclicReferences.ini  rb        '   human/en/Classes/MismatchProperties.iniY  rbY  B"      $   human/en/Classes/DefinedParentMP.ini  rb  Ý`      "   human/en/Classes/IsUpperFamily.ini  rb        (   human/en/Classes/OldStyleConstructor.ini  rb        /   human/en/Classes/InstantiatingAbstractClass.ini  rb        %   human/en/Classes/IsaMagicProperty.ini  rb        ,   human/en/Classes/ImplementIsForInterface.ini  rb  *c         human/en/Classes/Anonymous.ini&  rb&  mw٤      ,   human/en/Classes/AvoidOptionalProperties.ini  rb  q      (   human/en/Classes/DontUnsetProperties.ini
  rb
  ݤ      '   human/en/Classes/UndefinedConstants.ini)  rb)  D      !   human/en/Classes/UselessFinal.ini  rb  Ƥ      &   human/en/Classes/PropertyUsedBelow.ini  rb  `%      $   human/en/Classes/UndefinedMethod.ini  rb  6Fr      %   human/en/Classes/DynamicSelfCalls.ini  rb  t      $   human/en/Classes/UnresolvedCatch.ini  rb        "   human/en/Classes/UseInstanceof.ini  rb  PҤ      )   human/en/Classes/CouldBeAbstractClass.ini<  rb<  +9      -   human/en/Classes/RedefinedPrivateProperty.ini#  rb#  >      '   human/en/Classes/PromotedProperties.ini-  rb-  t,      '   human/en/Classes/ThisIsNotForStatic.ini  rb  _      +   human/en/Classes/WrongTypedPropertyInit.ini  rb  <7      '   human/en/Classes/CancelCommonMethod.ini  rb        .   human/en/Classes/DontSendThisInConstructor.ini  rb  z      )   human/en/Classes/AbstractOrImplements.ini  rb  p      )   human/en/Classes/MultipleDeclarations.ini  rb  N#ߤ      &   human/en/Classes/PropertyUsedAbove.ini  rb  q      "   human/en/Classes/StaticMethods.ini  rb  LeI          human/en/Classes/toStringPss.ini  rb  >      &   human/en/Classes/ConstantUsedBelow.ini  rb  aڤ      *   human/en/Classes/IncompatibleSignature.inib  rbb  (C      )   human/en/Classes/ConstVisibilityUsage.ini   rb   р      (   human/en/Classes/UsedPrivateProperty.inir  rbr  N$      $   human/en/Classes/MagicProperties.ini  rb  G      &   human/en/Classes/UnreachableMethod.ini  rb  Ҋ5ˤ          human/en/Classes/MagicMethod.iniy  rby  5[<ߤ      %   human/en/Classes/UndefinedClasses.ini  rb  1%      0   human/en/Classes/ImplementedMethodsArePublic.ini  rb  5I      '   human/en/Classes/RedefinedConstants.ini   rb   Gl      *   human/en/Classes/LocallyUnusedProperty.ini  rb  o      "   human/en/Classes/ConstantClass.ini  rb  'F      9   human/en/Classes/MultiplePropertyDeclarationOnOneLine.ini  rb  bN      &   human/en/Classes/UndefinedProperty.ini	  rb	  	9      !   human/en/Classes/FinalPrivate.ini  rb  B      (   human/en/Classes/CouldBeParentMethod.ini  rb  3c          human/en/Classes/IsNotFamily.ini  rb        $   human/en/Classes/MethodUsedBelow.ini  rb  X?      )   human/en/Classes/CouldBeClassConstant.ini$  rb$        %   human/en/Classes/StaticProperties.ini  rb  -      -   human/en/Classes/MultipleTraitOrInterface.ini  rb  {L      #   human/en/Classes/NoPublicAccess.ini
  rb
  m      (   human/en/Classes/MethodIsOverwritten.iniO  rbO  nФ      "   human/en/Classes/UnusedMethods.ini;  rb;  Sp	      $   human/en/Classes/UselessTypehint.ini+  rb+  Nc      %   human/en/Classes/UnfinishedObject.ini  rb  +      $   human/en/Classes/AmbiguousStatic.ini  rb  (PG      #   human/en/Classes/SameNameAsFile.ini6  rb6  'ͤ      )   human/en/Classes/UndefinedStaticclass.ini  rb  o         human/en/Classes/ClassUsage.ini  rb  qp          human/en/Classes/UsedMethods.ini  rb  e      %   human/en/Classes/SwappedArguments.ini  rb  ԣ      )   human/en/Classes/ShouldHaveDestructor.ini:  rb:           human/en/Classes/UsedClass.ini  rb  4         human/en/Classes/EmptyClass.ini  rb  '      '   human/en/Classes/StaticContainsThis.ini	  rb	  L	~         human/en/Classes/IsExtClass.ini  rb  	      $   human/en/Classes/FinalByOcramius.ini{  rb{  f*      !   human/en/Classes/CouldBeFinal.ini  rb  ]J3      )   human/en/Classes/PropertyCouldBeLocal.ini  rb  Yv|      %   human/en/Classes/ThisIsNotAnArray.inij  rbj  =7      (   human/en/Classes/DynamicConstantCall.ini  rb  r	      *   human/en/Classes/AmbiguousVisibilities.iniW  rbW  }@      (   human/en/Classes/MakeGlobalAProperty.iniK  rbK  迃ʤ      #   human/en/Classes/UnusedConstant.inio  rbo  ?          human/en/Classes/ParentFirst.ini:
  rb:
  E      '   human/en/Classes/ConstantDefinition.ini  rb  ,      '   human/en/Classes/CloneWithNonObject.ini  rb        "   human/en/Classes/ShouldUseSelf.ini  rb  c      !   human/en/Classes/TooManyFinds.ini  rb  N      $   human/en/Classes/PssWithoutClass.ini>  rb>  3xV      )   human/en/Classes/UnresolvedInstanceof.ini  rb  0      /   human/en/Classes/InheritedPropertyMustMatch.ini  rb  $      $   human/en/Classes/ShouldDeepClone.ini  rb  o].      *   human/en/Classes/MissingAbstractMethod.ini~  rb~  B      "   human/en/Classes/CouldBeStatic.ini  rb  K      %   human/en/Classes/HasMagicProperty.ini  rb  )դ      "   human/en/Classes/Abstractclass.ini  rb  kn      -   human/en/Classes/CouldBeProtectedConstant.ini}  rb}  \0J      (   human/en/Classes/UsedProtectedMethod.ini}  rb}           human/en/Classes/WrongCase.ini  rb  Ue캤      /   human/en/Classes/TypehintCyclicDependencies.inis  rbs  E19      &   human/en/Classes/OnlyStaticMethods.ini   rb   П         human/en/Classes/Finalclass.ini  rb  k      &   human/en/Classes/RedefinedProperty.ini  rb  E%      *   human/en/Classes/UnusedPrivateProperty.ini  rb  Y      &   human/en/Classes/UnresolvedClasses.ini  rb  5k      $   human/en/Classes/TooManyChildren.ini
	  rb
	  lh      /   human/en/Classes/CantOverwriteFinalConstant.ini  rb  Us      "   human/en/Classes/ReadonlyUsage.ini  rb  >;      1   human/en/Classes/NonStaticMethodsCalledStatic.ini
  rb
  k      "   human/en/Classes/AccessPrivate.iniT  rbT  2Ł      +   human/en/Classes/DependantAbstractClass.iniy  rby  M         human/en/Classes/AvoidUsing.ini0  rb0  7?      (   human/en/Classes/LocallyUsedProperty.ini  rb  )U#      $   human/en/Classes/MutualExtension.ini  rb  W㱤          human/en/Classes/MakeDefault.ini   rb   E6      &   human/en/Classes/IsInterfaceMethod.ini  rb        +   human/en/Classes/ScalarOrObjectProperty.ini  rb  kޚn         human/en/Classes/WeakType.ini   rb   o      ,   human/en/Classes/CouldBePrivateConstante.ini  rb  w      (   human/en/Classes/DynamicPropertyCall.ini  rb  ۰F      .   human/en/Classes/NoSelfReferencingConstant.iniJ  rbJ  ZN      ,   human/en/Classes/DifferentArgumentCounts.ini5  rb5  X      %   human/en/Classes/DefinedConstants.ini  rb  8      &   human/en/Classes/MissingVisibility.ini  rb        %   human/en/Classes/OverwrittenConst.ini  rb           human/en/Classes/DynamicNew.ini   rb   j0      !   human/en/Classes/CloningUsage.ini5  rb5  ]Kפ      $   human/en/Classes/DefinedProperty.ini  rb  h      ,   human/en/Classes/IncompatibleSignature74.ini6	  rb6	  R/      &   human/en/Classes/NoPSSOutsideClass.inii  rbi        )   human/en/Constants/MagicConstantUsage.ini  rb  ^4      +   human/en/Constants/ConstantStrangeNames.ini  rb  w      '   human/en/Constants/IsGlobalConstant.ini  rb        &   human/en/Constants/UnusedConstants.iniw  rbw  R_      $   human/en/Constants/ConstantUsage.ini  rb  A#      ,   human/en/Constants/ConstDefinePreference.ini  rb  !8o      "   human/en/Constants/StrangeName.ini&  rb&  !      &   human/en/Constants/DynamicCreation.ini  rb  dl      2   human/en/Constants/DefineInsensitivePreference.ini  rb  d.n      )   human/en/Constants/UndefinedConstants.ini  rb  n      '   human/en/Constants/VariableConstant.ini'  rb'  7,k      *   human/en/Constants/CustomConstantUsage.ini  rb  #1ۤ      &   human/en/Constants/CouldBeConstant.ini4  rb4  @)      1   human/en/Constants/MultipleConstantDefinition.ini  rb  58ڤ      '   human/en/Constants/CouldUseConstant.ini1  rb1        '   human/en/Constants/InconsistantCase.ini  rb  Bͣ      /   human/en/Constants/CaseInsensitiveConstants.inix  rbx  5Ѥ      1   human/en/Constants/CreatedOutsideItsNamespace.init  rbt  9a      '   human/en/Constants/BadConstantnames.ini  rb  c\      "   human/en/Constants/InvalidName.ini  rb  >      '   human/en/Constants/ConstRecommended.ini  rb  i%'¤      $   human/en/Constants/Constantnames.ini   rb   Yq      '   human/en/Constants/PhpConstantUsage.ini}  rb}  Em      $   human/en/Constants/IsPhpConstant.ini  rb  a      +   human/en/Constants/ConditionedConstants.ini9  rb9  =      $   human/en/Constants/IsExtConstant.iniJ  rbJ  SK      #   human/en/Composer/PackagesNames.ini   rb   'ˀ      %   human/en/Composer/UseComposerLock.ini   rb   B      %   human/en/Composer/IsComposerClass.ini   rb   tc      &   human/en/Composer/IsComposerNsname.ini  rb  }      !   human/en/Composer/UseComposer.ini   rb   ф1?         human/en/Composer/Autoload.ini   rb   	      )   human/en/Composer/IsComposerInterface.ini   rb   6l      $   human/en/Enums/UndefinedEnumcase.ini0  rb0  l(]      !   human/en/Enums/UnusedEnumCase.ini  rb  6         human/en/Features/ast.ini{  rb{  d         human/en/Features/parameter.ini  rb  <         human/en/Features/throwable.ini  rb  񌵤         human/en/Features/trait.ini  rb  &2ܤ         human/en/Features/resource.ini  rb  鳺         human/en/Features/exception.ini  rb  ̤         human/en/Features/default.ini  rb  AT         human/en/Features/this.iniq  rbq  n      &   human/en/Features/nested-attribute.ini  rb  'Ӥ      .   human/en/Features/nullsafe-object-operator.ini  rb  !          human/en/Features/framework.ini  rb  ,X      "   human/en/Features/array-spread.ini  rb  !&)         human/en/Features/coalesce.iniM  rbM  %      %   human/en/Features/escape-sequence.ini  rb  _      %   human/en/Features/return-typehint.ini  rb  +c      %   human/en/Features/scalar-typehint.ini  rb  Rت"         human/en/Features/signature.ini>  rb>  3٤      (   human/en/Features/array-curly-braces.ini  rb  Fޫ      $   human/en/Features/arrow-function.iniQ  rbQ  i      #   human/en/Features/pharexception.ini  rb  Z          human/en/Features/stringable.ini[  rb[  M         human/en/Features/directive.iniB  rbB        "   human/en/Features/null-ternary.ini  rb  p$      '   human/en/Features/directoryiterator.inih  rbh  H          human/en/Features/validation.ini_  rb_  f          human/en/Features/.DS_Store  rb  j m      $   human/en/Features/class-constant.ini  rb  7j         human/en/Features/stubs.ini  rb  A9          human/en/Features/yield-from.ini  rb  m         human/en/Features/parent.ini#  rb#  #6=Ӥ      '   human/en/Features/disable-functions.ini  rb  D[xq          human/en/Features/interfaces.ini   rb   7^z         human/en/Features/loop.ini  rb  c         human/en/Features/isset.ini"  rb"  J)]         human/en/Features/generator.ini  rb        "   human/en/Features/pdoexception.ini  rb  J@K      &   human/en/Features/dynamic-property.iniR  rbR  $A      #   human/en/Features/static-method.iniJ  rbJ  85         human/en/Features/false.ini&  rb&  Ua         human/en/Features/extension.ini  rb  C0j         human/en/Features/iterable.ini5  rb5  B         human/en/Features/namespace.ini  rb  3u      '   human/en/Features/default-parameter.iniG  rbG  oۤ         human/en/Features/readonly.init  rbt  /          human/en/Features/ternary.ini  rb  2         human/en/Features/foreach.ini  rb  =4r         human/en/Features/switch.inix  rbx  #         human/en/Features/pecl.iniu  rbu        &   human/en/Features/escape-character.ini)  rb)  od         human/en/Features/void.ini  rb  Iu         human/en/Features/typerror.ini  rb  룝      #   human/en/Features/jsonexception.ini~  rb~  e3o         human/en/Features/libsodium.inie  rbe  mH      %   human/en/Features/object-operator.ini|  rb|  Y¤         human/en/Features/pack.ini  rb  wҡ         human/en/Features/variadic.iniF  rbF  i%         human/en/Features/match.ini4  rb4  X*         human/en/Features/string.ini  rb  +{      !   human/en/Features/constructor.init  rbt  bi      !   human/en/Features/memoization.ini  rb  :      )   human/en/Features/unhandledmatcherror.ini  rb  QY      %   human/en/Features/global-variable.ini  rb  ]E      "   human/en/Features/dynamic-call.ini  rb  j      )   human/en/Features/late-static-binding.ini  rb  *8         human/en/Features/mixed.ini   rb   dU      )   human/en/Features/divisionbyzeroerror.ini	  rb	  ä         human/en/Features/object.ini  rb  _         human/en/Features/operator.ini  rb  Yy      +   human/en/Features/function-subscripting.ini.  rb.  xݤ      (   human/en/Features/new-in-initializer.ini  rb        &   human/en/Features/special-typehint.iniL  rbL  ;          human/en/Features/destructor.ini  rb  2d      $   human/en/Features/binary-integer.ini  rb  Z      '   human/en/Features/promoted-property.iniG  rbG  6O         human/en/Features/openssl.ini  rb  S&         human/en/Features/typehint.iniV  rbV  Z      !   human/en/Features/hexadecimal.ini  rb  l_?ޤ      /   human/en/Features/type-declaration-property.ini  rb  E         human/en/Features/private.ini  rb  en      %   human/en/Features/type-covariance.ini`  rb`  {~,         human/en/Features/ssl.iniU  rbU  2$7         human/en/Features/error.ini  rb   B&         human/en/Features/date.ini  rb  &!          human/en/Features/sanitation.iniF  rbF  vb         human/en/Features/property.iniH  rbH  kؤ         human/en/Features/random.iniX  rbX         (   human/en/Features/argumentcounterror.ini  rb  e      %   human/en/Features/static-property.iniU  rbU   W         human/en/Features/null.ini  rb  4`      '   human/en/Features/intersection-type.ini  rb  u      )   human/en/Features/type-contravariance.iniV  rbV  7;      *   human/en/Features/first-class-callable.iniM  rbM  dRH      %   human/en/Features/namespace-alias.ini  rb  B         human/en/Features/tls.iniN  rbN  1M         human/en/Features/cli.ini  rb  >a)1         human/en/Features/attribute.iniw  rbw  v7I          human/en/Features/comparison.ini  rb           human/en/Features/stdclass.inis  rbs  `         human/en/Features/setter.inii  rbi  [0      '   human/en/Features/numeric-separator.ini  rb  /t.         human/en/Features/reference.ini  rb        %   human/en/Features/arithmeticerror.ini  rb  ɥ         human/en/Features/glob.ini*  rb*  A         human/en/Features/method.init  rbt  &         human/en/Features/xml.ini  rb  Vy         human/en/Features/clone.iniC  rbC  Q         human/en/Features/https.ini  rb  A$         human/en/Features/use.ini  rb  .\r      !   human/en/Features/superglobal.ini  rb  jؤ         human/en/Features/enum-case.ini  rb        .   human/en/Features/invalidargumentexception.ini	  rb	  S         human/en/Features/private.php  rb  IS      %   human/en/Features/named-parameter.ini  rb  jj         human/en/Features/echo.inia  rba           human/en/Features/inclusion.ini  rb           human/en/Features/exponent.ini  rb  %7         human/en/Features/session.ini  rb  !:      .   human/en/Features/unexpectedvalueexception.ini  rb  >H)      #   human/en/Features/interpolation.ini	  rb	  9Q      $   human/en/Features/trailing-comma.ini  rb  =P         human/en/Features/label.inie  rbe  0o         human/en/Features/getter.ini?  rb?  1ny         human/en/Features/float.ini  rb  a3         human/en/Features/compact.ini  rb  D         human/en/Features/enum.ini"  rb"  K         human/en/Features/nullable.iniM  rbM  oj:         human/en/Features/empty.inie  rbe  &H         human/en/Features/arrays.ini  rb  *      *   human/en/Features/predefined-exception.ini+  rb+  -ob      &   human/en/Features/dynamic-variable.iniI  rbI  WI(         human/en/Features/function.ini  rb  !}         human/en/Features/native.ini
  rb
  ~-       $   human/en/Features/magic-constant.ini  rb  tդ         human/en/Features/throw.ini9  rb9  |k_      %   human/en/Features/disable-classes.ini  rb  S:f      '   human/en/Features/variable-variable.ini  rb  fŤ         human/en/Features/try-catch.ini  rb  u          human/en/Features/union-type.ini  rb  o4         human/en/Features/array.inib  rbb  \s         human/en/Features/phpinfo.ini  rb  醉         human/en/Features/real.ini  rb  Q         human/en/Features/linting.ini  rb  j$i         human/en/Features/var.ini  rb  )           human/en/Features/visibility.ini  rb  Ф         human/en/Features/interface.iniI  rbI  bf         human/en/Features/cookie.ini  rb  P      /   human/en/Features/scope-resolution-operator.inin  rbn  	^         human/en/Features/assertion.ini  rb            human/en/Features/instanceof.ini  rb  ?0         human/en/Features/extract.iniX  rbX  o         human/en/Features/return.ini1  rb1  G,      (   human/en/Features/language-construct.ini  rb           human/en/Features/yield.ini  rb           human/en/Features/regex.ini%  rb%  Ja@      *   human/en/Features/returntypewillchange.ini  rb  C         human/en/Features/continue.ini  rb  M      *   human/en/Features/final-class-constant.iniw  rbw  ,F      !   human/en/Features/assignation.ini:  rb:  L^         human/en/Features/if-then.ini  rb  2v          human/en/Features/valueerror.ini  rb  z      !   human/en/Features/indentation.ini  rb  /      *   human/en/Features/fully-qualified-name.ini  rb  	         human/en/Features/abstract.ini  rb  U&^*         human/en/Features/closure.iniU  rbU  yФ      !   human/en/Features/class-alias.ini  rb  }1      '   human/en/Features/short-assignation.ini  rb  <R          human/en/Features/implements.iniG  rbG  J         human/en/Features/sleep.iniX  rbX  (t         human/en/Features/unset.ini  rb  2V         human/en/Features/case.inih  rbh  .dw         human/en/Features/self.ini\  rb\  	]      "   human/en/Features/magic-method.inip  rbp  x^E         human/en/Features/spaceship.iniS  rbS  Tg         human/en/Features/eval.ini  rb  gM         human/en/Features/never.ini  rb  3      !   human/en/Features/parenthesis.ini  rb  @U_      "   human/en/Features/php-variable.init  rbt           human/en/Features/hash.ini  rb  Ec         human/en/Features/insteadof.iniI  rbI  [2         human/en/Features/overwrite.ini  rb  [         human/en/Features/countable.ini  rb  H         human/en/Features/global.inir  rbr  :         human/en/Features/typeerror.ini  rb  &e#         human/en/Features/plus.ini  rb  Nk         human/en/Features/static.ini=  rb=  B-\      "   human/en/Features/cryptography.inih  rbh  2       $   human/en/Features/never-typehint.ini  rb  lu         human/en/Features/final.iniS  rbS  {         human/en/Features/goto.ini  rb  |{         human/en/Features/constant.ini  rb        #   human/en/Features/compact-array.ini  rb  Ĵy      !   human/en/Features/inheritance.ini  rb  tp      $   human/en/Features/logicexception.iniR  rbR  `u         human/en/Features/variable.ini  rb  &         human/en/Features/nullsafe.ini~  rb~  *<ݤ         human/en/Features/as.iniV  rbV  K
x         human/en/Features/callable.iniU  rbU  }❤      *   human/en/Features/string-interpolation.ini  rb  v         human/en/Features/psr.iniD  rbD  "         human/en/Features/cast.ini  rb  4      %   human/en/Features/static-variable.ini3  rb3           human/en/Features/integer.ini^  rb^  v         human/en/Features/class.iniU  rbU  }❤      /   human/en/Features/class-constant-visibility.ini  rb  ˤ         human/en/Features/argument.ini  rb  :      0   human/en/Features/constant-scalar-expression.ini  rb  k<         human/en/Cobbler/.DS_Store   rb         -   human/en/Cobbler/Structures/SwitchToMatch.ini  rb  /-      .   human/en/Cobbler/Structures/RenameFunction.ini  rb  {      2   human/en/Cobbler/Structures/RenameFunctionCall.inin  rbn  uC      0   human/en/Cobbler/Structures/RenameMethodcall.ini7  rb7        *   human/en/Cobbler/Structures/RemoveCode.ini6  rb6  o      .   human/en/Cobbler/Structures/ArrayToBracket.ini]  rb]  d      .   human/en/Cobbler/Structures/RemoveNoScream.ini  rb  Cy8      .   human/en/Cobbler/Structures/RemoveVariable.ini+  rb+  A&      ,   human/en/Cobbler/Structures/PlusOneToPre.ini  rb  o      1   human/en/Cobbler/Structures/RemoveParenthesis.ini  rb        )   human/en/Cobbler/Structures/PostToPre.ini  rb  t      0   human/en/Cobbler/Structures/ArrayKeysSpeedup.inio  rbo  X      +   human/en/Cobbler/Structures/AddNoScream.ini  rb  `A7      +   human/en/Cobbler/Classes/RemoveReadonly.ini$  rb$  E      (   human/en/Cobbler/Classes/VarToPublic.ini  rb  ~D(      -   human/en/Cobbler/Classes/RemoveVisibility.ini  rb   [      5   human/en/Cobbler/Classes/SplitPropertyDefinitions.ini  rb  J      )   human/en/Cobbler/Classes/RemoveMethod.ini  rb  3Y          human/en/Cobbler/Utils/Multi.ini  rb  ]      )   human/en/Cobbler/Namespaces/GatherUse.ini;  rb;  $      )   human/en/Cobbler/Namespaces/RemoveUse.ini  rb        (   human/en/Cobbler/Namespaces/UseAlias.ini  rb  י      /   human/en/Cobbler/Attributes/RemoveAttribute.ini  rb  o<      ,   human/en/Cobbler/Attributes/CreatePhpdoc.inio  rbo  Y      *   human/en/Cobbler/Functions/SetTypeVoid.ini  rb  ޹>      .   human/en/Cobbler/Functions/RenameParameter.iniI  rbI  `	      1   human/en/Cobbler/Functions/MakeStaticFunction.ini  rb        *   human/en/Cobbler/Functions/RemoveTypes.iniR  rbR  _ N      +   human/en/Cobbler/Functions/SetTypehints.ini'  rb'  Т      6   human/en/Cobbler/Functions/RemoveStaticFromClosure.ini  rb  
KŤ      *   human/en/Cobbler/Functions/SetNullType.ini  rb  լ         human/en/Utils/Selector.ini  rb  4      #   human/en/Php/NeverTypehintUsage.ini  rb  (         human/en/Php/MixedUsage.ini  rb  ;      &   human/en/Php/Php74RemovedDirective.iniT  rbT  U          human/en/Php/UsePathinfoArgs.inix  rbx  :`ݤ      "   human/en/Php/IncomingVariables.iniw  rbw        #   human/en/Php/FirstClassCallable.ini2  rb2  d      *   human/en/Php/MustCallParentConstructor.inim  rbm  bJ         human/en/Php/NewExponent.ini9  rb9  wLݤ      !   human/en/Php/NoListWithString.ini  rb  rU         human/en/Php/AvoidReal.ini  rb        %   human/en/Php/NoReturnForGenerator.ini  rb        !   human/en/Php/LogicalInLetters.ini  rb            human/en/Php/Coalesce.ini  rb  "      "   human/en/Php/ConcatAndAddition.ini  rb  ͤ          human/en/Php/ListShortSyntax.iniz  rbz  Q_      %   human/en/Php/ThrowWasAnExpression.inip  rbp  #         human/en/Php/YieldUsage.ini  rb  r          human/en/Php/AutoloadUsage.ini  rb        '   human/en/Php/ClassFunctionConfusion.ini  rb  :8          human/en/Php/DirectivesUsage.ini2  rb2  情ݤ      0   human/en/Php/NestedTernaryWithoutParenthesis.iniZ  rbZ  eh      "   human/en/Php/MissingMagicIsset.ini  rb  1kQ         human/en/Php/Deprecated.ini  rb  ߮      %   human/en/Php/PHP81scalartypehints.iniN  rbN  (dJ          human/en/Php/DeclareTicks.ini9  rb9  ^(         human/en/Php/MiddleVersion.ini   rb   H         human/en/Php/IncomingValues.ini  rb  Nk      %   human/en/Php/ReservedMatchKeyword.iniw  rbw  .@         human/en/Php/UseStrContains.ini  rb  '[̤          human/en/Php/UseNullableType.ini`  rb`  5U         human/en/Php/BetterRand.ini	  rb	  <      &   human/en/Php/UnpackingInsideArrays.ini:  rb:  t         human/en/Php/UseWeb.iniQ  rbQ  h      &   human/en/Php/Php71RemovedDirective.ini   rb   t7      '   human/en/Php/AvoidMbDectectEncoding.ini  rb  ~M      #   human/en/Php/DetectCurrentClass.ini  rb        )   human/en/Php/ForeachDontChangePointer.ini<  rb<           human/en/Php/DirectiveName.ini3  rb3  yuy         human/en/Php/ReservedNames.ini  rb  J>      #   human/en/Php/UnknownPcre2Option.ini3  rb3  W ̤         human/en/Php/IsINF.ini  rb  Q'      &   human/en/Php/Php70RemovedDirective.ini   rb   Jq      !   human/en/Php/RawPostDataUsage.ini  rb  t(      $   human/en/Php/ClassConstWithArray.ini)  rb)  WѤ         human/en/Php/ExponentUsage.ini  rb  \      %   human/en/Php/Php74mbstrrpos3rdArg.ini8  rb8           human/en/Php/CastUnsetUsage.iniL  rbL        *   human/en/Php/Php81IntersectionTypehint.ini  rb        $   human/en/Php/StringIntComparison.iniQ  rbQ        #   human/en/Php/TooManyNativeCalls.ini  rb  }9      "   human/en/Php/ListWithReference.ini  rb  ɤ      &   human/en/Php/GroupUseTrailingComma.ini\  rb\  F      "   human/en/Php/Php72NewFunctions.ini  rb  G
          human/en/Php/EllipsisUsage.iniW  rbW  0      "   human/en/Php/NoMoreCurlyArrays.ini  rb  xPB         human/en/Php/PearUsage.ini`  rb`  K      &   human/en/Php/Php80RemovedDirective.iniS  rbS  |H      "   human/en/Php/AlternativeSyntax.ini  rb  ؏a      -   human/en/Php/NativeClassTypeCompatibility.ini  rb  8ڤ      #   human/en/Php/NoStringWithAppend.ini  rb            human/en/Php/FlexibleHeredoc.ini~  rb~  1         human/en/Php/NoCastToInt.ini  rb  C:T         human/en/Php/YieldFromUsage.ini  rb  ^B          human/en/Php/DeclareEncoding.ini4  rb4  Ê;         human/en/Php/NeverKeyword.iniz  rbz  B阤      "   human/en/Php/Php56NewFunctions.ini[  rb[  M<      &   human/en/Php/Php81RemovedDirective.ini  rb  ޤ      ,   human/en/Php/Php80NamedParameterVariadic.ini  rb  (x         human/en/Php/HashAlgos54.ini  rb  2tŤ      (   human/en/Php/SetExceptionHandlerPHP7.ini  rb  I)      %   human/en/Php/PHP72scalartypehints.inir  rbr  	F         human/en/Php/MixedKeyword.ini)  rb)  qz         human/en/Php/TryCatchUsage.ini  rb  g          human/en/Php/UseCookies.iniO  rbO        $   human/en/Php/GroupUseDeclaration.ini  rb  Ͳ         human/en/Php/UseStdclass.iniq  rbq  r          human/en/Php/NewInitializers.ini  rb  KfW      %   human/en/Php/DeprecateDollarCurly.ini  rb  T         human/en/Php/ShellFavorite.inii  rbi  :b         human/en/Php/EmptyList.ini  rb  M{          human/en/Php/Php70NewClasses.ini  rb  .      "   human/en/Php/OveriddenFunction.iniS  rbS  
1n         human/en/Php/StrtrArguments.ini  rb        1   human/en/Php/CantUseReturnValueInWriteContext.ini  rb  _      "   human/en/Php/Php73NewFunctions.ini  rb   |      !   human/en/Php/SessionVariables.ini  rb  ڤ      -   human/en/Php/ReflectionExportIsDeprecated.ini\  rb\  <Τ          human/en/Php/Php74NewClasses.ini  rb  ᰤ      "   human/en/Php/MissingSubpattern.ini	  rb	  ,<         human/en/Php/Labelnames.ini\  rb\  dV(      (   human/en/Php/JsonSerializeReturnType.ini  rb  0tXj      ,   human/en/Php/WrongAttributeConfiguration.ini  rb  $         human/en/Php/Incompilable.ini  rb  b.         human/en/Php/CastingUsage.ini  rb  7P<0      )   human/en/Php/ConstantScalarExpression.ini;  rb;  n0      %   human/en/Php/Php80RemovedConstant.ini  rb  y      &   human/en/Php/Php54RemovedFunctions.ini  rb  ļ      #   human/en/Php/TypedPropertyUsage.ini
  rb
  gB         human/en/Php/FinalConstant.ini  rb  E         human/en/Php/UsesEnv.ini2  rb2           human/en/Php/CryptoUsage.ini  rb  !u      #   human/en/Php/Php72ObjectKeyword.ini  rb  e      "   human/en/Php/Php74NewFunctions.ini  rb           human/en/Php/SetHandlers.ini  rb  v0W          human/en/Php/MethodCallOnNew.ini  rb  ({      &   human/en/Php/Php55RemovedFunctions.inie  rbe  gbI         human/en/Php/Prints.iniU  rbU  u          human/en/Php/ListWithAppends.ini>  rb>  y      +   human/en/Php/LetterCharsLogicalFavorite.ini  rb  )      "   human/en/Php/ShouldUseFunction.ini-  rb-  l.         human/en/Php/HashAlgos53.ini  rb  @         human/en/Php/UseClassAlias.ini  rb  &ip         human/en/Php/CloseTags.ini  rb  ̭Ƥ         human/en/Php/FilesFullPath.ini  rb  J          human/en/Php/GlobalsVsGlobal.ini,  rb,  sFʪ         human/en/Php/debugInfoUsage.ini  rb  h띤         human/en/Php/ErrorLogUsage.ini  rb  Wi         human/en/Php/IsNAN.ini  rb  ,#7      "   human/en/Php/UseContravariance.ini  rb  -%p         human/en/Php/UseAttributes.ini8  rb8  *      &   human/en/Php/ReturnWithParenthesis.ini  rb  h}      #   human/en/Php/ClosureThisSupport.ini  rb        !   human/en/Php/UpperCaseKeyword.iniM  rbM  )T      "   human/en/Php/TriggerErrorUsage.inio  rbo  j      %   human/en/Php/PHP71scalartypehints.ini6  rb6  zEä         human/en/Php/TrailingComma.ini  rb  KŤ      #   human/en/Php/AvoidGetobjectVars.ini  rb  z      "   human/en/Php/ReservedKeywords7.ini`  rb`  k\ؤ         human/en/Php/UseSetCookie.inio  rbo  
v      %   human/en/Php/UnicodeEscapePartial.ini  rb  a      '   human/en/Php/DontPolluteGlobalSpace.iniW  rbW  xlc          human/en/Php/NoClassInGlobal.ini  rb        '   human/en/Php/VersionCompareOperator.ini  rb  RV      "   human/en/Php/IsnullVsEqualNull.ini]  rb]  a^      %   human/en/Php/Php81RemovedConstant.ini%  rb%  b5      $   human/en/Php/NamedParameterUsage.ini	  rb	  wrt          human/en/Php/DefineWithArray.ini  rb  o֫         human/en/Php/HashAlgos74.iniw  rbw  ެ٤         human/en/Php/HashAlgos.ini  rb  8H          human/en/Php/Php72NewClasses.iniX  rbX  U         human/en/Php/EchoTagUsage.ini   rb         "   human/en/Php/Php55NewFunctions.ini[  rb[  O4      %   human/en/Php/ShortOpenTagRequired.iniO  rbO  Z?R      ,   human/en/Php/GlobalWithoutSimpleVariable.ini  rb  ()      %   human/en/Php/UseDateTimeImmutable.ini  rb  *?9^      #   human/en/Php/ScalarAreNotArrays.ini  rb  `      !   human/en/Php/PregMatchAllFlag.ini  rb  ?{      '   human/en/Php/PHP73LastEmptyArgument.ini  rb  E#         human/en/Php/Haltcompiler.ini  rb  zR*      %   human/en/Php/PHP70scalartypehints.ini  rb        &   human/en/Php/NoReferenceForTernary.iniv  rbv  F4         human/en/Php/UseMatch.ini  rb  z      !   human/en/Php/NoSubstrMinusOne.ini  rb        $   human/en/Php/UnicodeEscapeSyntax.iniV  rbV  hO         human/en/Php/ConstWithArray.ini  rb  6t      "   human/en/Php/DeclareStrictType.ini  rb  碟         human/en/Php/Argon2Usage.inir  rbr  '/      '   human/en/Php/SignatureTrailingComma.ini  rb        %   human/en/Php/ShouldUseArrayFilter.ini  rb  t         human/en/Php/Gotonames.ini  rb  V      "   human/en/Php/Php71NewFunctions.ini  rb  zΤ      $   human/en/Php/Php80VariableSyntax.ini  rb  ǹ1         human/en/Php/UseObjectApi.ini	  rb	  ~!      $   human/en/Php/ReturnTypehintUsage.iniN  rbN  _      "   human/en/Php/Php74NewDirective.ini  rb  ݤ         human/en/Php/AssignAnd.inin  rbn  q         human/en/Php/ListWithKeys.ini+  rb+  @A      %   human/en/Php/CloseTagsConsistency.ini_  rb_  s      )   human/en/Php/CallingStaticTraitMethod.inie  rbe  l      !   human/en/Php/StaticclassUsage.ini  rb  O         human/en/Php/AssertionUsage.ini  rb           human/en/Php/CaseForPSS.ini  rb  d      )   human/en/Php/OnlyVariableForReference.ini  rb  7OZ      '   human/en/Php/SpreadOperatorForArray.iniN  rbN  b'>      %   human/en/Php/Crc32MightBeNegative.ini  rb  s      $   human/en/Php/UseTrailingUseComma.ini*  rb*  -         human/en/Php/IdnUts46.inir  rbr  a1         human/en/Php/ForeachObject.iniv  rbv  .'      "   human/en/Php/CompactInexistant.ini  rb  >         human/en/Php/NotScalarType.ini  rb  Ͻv      "   human/en/Php/Php81NewFunctions.ini  rb           human/en/Php/UseBrowscap.iniy  rby  t0Ϥ      $   human/en/Php/UseNullSafeOperator.ini~  rb~  ^[:         human/en/Php/UsePathinfo.iniy  rby  0      $   human/en/Php/RestrictGlobalUsage.ini  rb  r          human/en/Php/HashUsesObjects.ini  rb  zWo      "   human/en/Php/Php72NewConstants.ini  rb  ͤ      &   human/en/Php/IntegerSeparatorUsage.ini  rb  D7_          human/en/Php/FailingAnalysis.ini   rb   t\դ      !   human/en/Php/oldAutoloadUsage.ini  rb  a         human/en/Php/FopenMode.ini3  rb3  u*         human/en/Php/DateFormats.ini  rb  !      !   human/en/Php/PhpErrorMsgUsage.ini  rb  ,      +   human/en/Php/WrongTypeForNativeFunction.iniu  rbu  a          human/en/Php/Php71NewClasses.inia  rba  ec         human/en/Php/DlUsage.ini  rb  ؉\         human/en/Php/FalseToArray.iniP  rbP  qW         human/en/Php/HashAlgos71.iniw  rbw  t      -   human/en/Php/NoReferenceForStaticProperty.ini  rb  O      +   human/en/Php/CouldUsePromotedProperties.iniP  rbP  QӤ      &   human/en/Php/InternalParameterType.ini  rb  m`         human/en/Php/Password55.ini  rb  4H      )   human/en/Php/AssertFunctionIsReserved.ini  rb  j5         human/en/Php/SerializeMagic.ini  rb  |FtǤ      #   human/en/Php/Php80UnionTypehint.ini  rb  T      !   human/en/Php/ShouldPreprocess.iniR  rbR  cO      )   human/en/Php/OpensslEncryptAlgoChange.ini  rb  wW          human/en/Php/UseGetDebugType.ini  rb  +Ү      &   human/en/Php/Php80RemovesResources.ini2  rb2  q      "   human/en/Php/IssetMultipleArgs.ini  rb  h1?      $   human/en/Php/CouldUseIsCountable.ini  rb  "ٱ      "   human/en/Php/Php71microseconds.ini  rb  <V>      &   human/en/Php/MultipleDeclareStrict.iniN  rbN  :Eg      &   human/en/Php/Php81RemovesResources.ini  rb  DI!          human/en/Php/NoNullForNative.ini:  rb:  Ҽ      !   human/en/Php/CookiesVariables.ini  rb  wפ      &   human/en/Php/Php74RemovedFunctions.iniS  rbS        #   human/en/Php/Php80OnlyTypeHints.ini  rb  a扤      $   human/en/Php/ScalarTypehintUsage.iniF  rbF  Yܖˤ      "   human/en/Php/ShouldUseCoalesce.inid  rbd  Q         human/en/Php/DeclareStrict.ini  rb  >IĤ         human/en/Php/UnsetOrCast.ini  rb  :{      /   human/en/Php/AvoidSetErrorHandlerContextArg.inir  rbr  ܐ)}      "   human/en/Php/Php80NewFunctions.ini
  rb
  <X턤      !   human/en/Php/Php74Deprecation.inir  rbr  ,_DR          human/en/Php/PathinfoReturns.ini0  rb0  P8*         human/en/Php/SafePhpvars.ini  rb  A¤         human/en/Php/ThrowUsage.ini  rb  lN      *   human/en/Php/ArrayKeyExistsWithObjects.inip  rbp   /      !   human/en/Php/SuperGlobalUsage.ini  rb  ⸤      '   human/en/Php/UseSessionStartOptions.iniD  rbD  +F=      &   human/en/Php/Php70RemovedFunctions.ini  rb        #   human/en/Php/Php7RelaxedKeyword.iniK  rbK  =5      '   human/en/Php/ParenthesisAsParameter.ini  rb  ߬       %   human/en/Php/ShouldUseArrayColumn.ini  rb  ,I       "   human/en/Php/DirectCallToClone.ini  rb  8oɤ         human/en/Php/UsortSorting.inip  rbp  6"         human/en/Php/UseCovariance.ini  rb  ,-         human/en/Php/EnumUsage.ini  rb  ߇         human/en/Php/CoalesceEqual.ini  rb  NX      "   human/en/Php/Php74NewConstants.ini  rb  [9R      !   human/en/Php/TryMultipleCatch.ini  rb  cr      "   human/en/Php/UpperCaseFunction.ini  rb  Y+      %   human/en/Php/Php74ReservedKeyword.ini  rb  >`Q         human/en/Php/IsAWithString.ini  rb  	)=      "   human/en/Php/Php54NewFunctions.iniM  rbM  T:.      &   human/en/Php/Php72RemovedFunctions.ini  rb  -"      &   human/en/Php/Php81RemovedFunctions.ini  rb  w~         human/en/Php/ImplodeOneArg.ini-  rb-  9      #   human/en/Php/Php70NewInterfaces.ini   rb   fm|         human/en/Php/Assumptions.ini,  rb,  #ed         human/en/Php/UseCli.ini   rb   ]C      #   human/en/Php/FilterToAddSlashes.ini  rb  ,3      "   human/en/Php/Php70NewFunctions.ini  rb  &      !   human/en/Php/Php72Deprecation.ini  rb  Nä      &   human/en/Php/Php80RemovedFunctions.ini  rb        %   human/en/Php/PHP80scalartypehints.ini  rb  ʯ      &   human/en/Php/Php73RemovedFunctions.ini-  rb-  Mպe      *   human/en/Exceptions/CaughtButNotThrown.init  rbt  E      *   human/en/Exceptions/OverwriteException.ini  rb        *   human/en/Exceptions/ThrowRawExceptions.ini)  rb)  S      .   human/en/Exceptions/CatchUndefinedVariable.ini  rb  rmv      %   human/en/Exceptions/MultipleCatch.ini  rb  RG      *   human/en/Exceptions/UncaughtExceptions.ini  rb  _Ѥ      $   human/en/Exceptions/UselessCatch.ini	  rb	  .^g      )   human/en/Exceptions/DefinedExceptions.ini  rb  ߤ      #   human/en/Exceptions/CouldUseTry.ini  rb  I޵         human/en/Exceptions/CatchE.ini  rb  'Ť      %   human/en/Exceptions/AlreadyCaught.ini	  rb	  OĤ      '   human/en/Exceptions/LongPreparation.ini  rb  YΒq      '   human/en/Exceptions/ForgottenThrown.ini  rb  :~      !   human/en/Exceptions/CantThrow.ini*  rb*  #      (   human/en/Exceptions/CaughtExceptions.ini_  rb_  W]          human/en/Exceptions/Unthrown.ini+  rb+  &      /   human/en/Exceptions/UnusedExceptionVariable.iniq  rbq  ѭ      (   human/en/Exceptions/ThrownExceptions.ini^  rb^  q      )   human/en/Exceptions/ThrowFunctioncall.ini  rb  㜤      %   human/en/Exceptions/LargeTryBlock.inid  rbd  zU      &   human/en/Exceptions/IsPhpException.ini9  rb9  5b          human/en/Exceptions/Rethrown.ini  rb  {T         human/en/Extensions/Extyar.ini  rb  Ó4          human/en/Extensions/Extevent.ini  rb  'kDФ          human/en/Extensions/Extcyrus.ini  rb  V      #   human/en/Extensions/Extreadline.ini  rb  5ݳ         human/en/Extensions/Extexif.ini  rb  *܄      "   human/en/Extensions/Extphalcon.ini  rb  '1         human/en/Extensions/Extldap.ini  rb  ,&#         human/en/Extensions/Extast.ini  rb  4      "   human/en/Extensions/Extseaslog.ini  rb  t!#         human/en/Extensions/Extsnmp.ini  rb  Z         human/en/Extensions/Extiis.ini  rb           human/en/Extensions/Extmath.ini  rb  cv         human/en/Extensions/Extv8js.ini  rb            human/en/Extensions/Extmhash.iniE  rbE  F	)      #   human/en/Extensions/Extprotobuf.iniz	  rbz	  ө      &   human/en/Extensions/Exttokyotyrant.ini  rb  Z"j          human/en/Extensions/Extpcntl.ini  rb  A\^         human/en/Extensions/Extvips.ini^  rb^  5         human/en/Extensions/Extfann.ini   rb   n      $   human/en/Extensions/Extzookeeper.ini  rb  K>Ȥ          human/en/Extensions/Extnsapi.ini,  rb,  ݤC      !   human/en/Extensions/Extsqlsrv.ini  rb  w0C         human/en/Extensions/Extdate.inix  rbx  -         human/en/Extensions/Extlzf.ini  rb  ,b      !   human/en/Extensions/Extswoole.ini  rb  蚤      !   human/en/Extensions/Extsqlite.ini  rb  m         human/en/Extensions/Extob.ini  rb  10      "   human/en/Extensions/Extsockets.ini  rb  n          human/en/Extensions/Extgeoip.ini~  rb~  +N      $   human/en/Extensions/Extsimplexml.ini  rb  sX~         human/en/Extensions/Extftp.ini  rb  .#      #   human/en/Extensions/Extwincache.ini  rb  4Q.      $   human/en/Extensions/Extwikidiff2.ini  rb        "   human/en/Extensions/Extsuhosin.ini  rb  g7         human/en/Extensions/Exteio.iniS  rbS  &g      !   human/en/Extensions/Extcsprng.ini  rb  DV      $   human/en/Extensions/Extmemcached.inix  rbx  bߤ      $   human/en/Extensions/Extlibsodium.iniw  rbw  iĀ          human/en/Extensions/Extxdiff.iniP  rbP        !   human/en/Extensions/Extgender.ini  rb  s]P      !   human/en/Extensions/Extrecode.ini(  rb(  |bS|      $   human/en/Extensions/Exttokenizer.ini@  rb@  +0         human/en/Extensions/Extdom.iniq  rbq            human/en/Extensions/Extbzip2.ini  rb  \4      #   human/en/Extensions/Extlibevent.ini  rb  |mb-      "   human/en/Extensions/Extrdkafka.ini  rb  XZ         human/en/Extensions/Extyaml.ini?  rb?  2Ɣ      #   human/en/Extensions/Extmbstring.inim  rbm  毻A      "   human/en/Extensions/Extsession.ini  rb  er:פ          human/en/Extensions/Extarray.inib  rbb            human/en/Extensions/Extcmark.iniz  rbz  n겭         human/en/Extensions/Extcom.ini  rb  j      !   human/en/Extensions/Extsphinx.ini  rb  l      #   human/en/Extensions/Extigbinary.ini<  rb<  bA         human/en/Extensions/Extsoap.ini  rb  U         human/en/Extensions/Extzmq.ini  rb            human/en/Extensions/Extasync.ini  rb  :*         human/en/Extensions/Extzlib.iniZ  rbZ  YE       !   human/en/Extensions/Extxcache.ini'  rb'  3&u      "   human/en/Extensions/Extncurses.iniI  rbI            human/en/Extensions/Extuopz.ini  rb  M      #   human/en/Extensions/Extmemcache.ini  rb  }      #   human/en/Extensions/Extstandard.ini  rb  JE         human/en/Extensions/Extpcre.ini  rb  y      "   human/en/Extensions/Extvarnish.ini  rb  ӄԤ      #   human/en/Extensions/Extparsekit.inio  rbo  YFj         human/en/Extensions/Extgmp.ini  rb  ,ᴤ      !   human/en/Extensions/Extffmpeg.ini  rb  >ώ      "   human/en/Extensions/Extgmagick.inix  rbx  2{C         human/en/Extensions/Extfpm.ini  rb  ?q/      !   human/en/Extensions/Extrunkit.ini  rb  iӣ      !   human/en/Extensions/Extfilter.ini  rb  \         human/en/Extensions/Extodbc.ini  rb  w      !   human/en/Extensions/Extlapack.ini  rb  Ga      !   human/en/Extensions/Extxdebug.ini  rb  jǯ      !   human/en/Extensions/Extmysqli.ini  rb  %!t         human/en/Extensions/Extimap.ini  rb  O'^      "   human/en/Extensions/Extleveldb.ini  rb  	      "   human/en/Extensions/Extopcache.ini  rb  .ꖤ         human/en/Extensions/Extwasm.ini  rb            human/en/Extensions/Extev.iniT  rbT  k      $   human/en/Extensions/Extproctitle.ini  rb  y<      !   human/en/Extensions/Extstring.ini  rb  N*C         human/en/Extensions/Extds.ini  rb  k         human/en/Extensions/Extdio.ini  rb  md^A      !   human/en/Extensions/Extxhprof.iniY  rbY  K          human/en/Extensions/Extibase.inia  rba  "
B          human/en/Extensions/Extmongo.ini  rb  ov         human/en/Extensions/Extyis.iniJ  rbJ  [F      "   human/en/Extensions/Extdecimal.iniY  rbY   04      "   human/en/Extensions/Extweakref.inid  rbd  /d      $   human/en/Extensions/Extmailparse.ini  rb           human/en/Extensions/Extxml.ini  rb  Z         human/en/Extensions/Extinfo.ini  rb  ƑM         human/en/Extensions/Extspl.ini6  rb6  G         human/en/Extensions/Extfdf.ini  rb  K¤<      !   human/en/Extensions/Exthrtime.ini  rb  oݤ         human/en/Extensions/Exthash.iniy  rby  tT         human/en/Extensions/Extgd.ini  rb  b         human/en/Extensions/Extphar.ini  rb  h          human/en/Extensions/Extmysql.ini  rb  **      $   human/en/Extensions/Extxmlwriter.inif  rbf  z1         human/en/Extensions/Extsem.ini  rb           human/en/Extensions/Extrar.ini  rb  :      !   human/en/Extensions/Extpspell.iniN  rbN  f          human/en/Extensions/Extxattr.ini  rb  Ɠv      &   human/en/Extensions/Extzendmonitor.ini  rb  "zܤ      "   human/en/Extensions/Extgearman.ini  rb  Q؟
      %   human/en/Extensions/Extreflection.ini  rb  '	      "   human/en/Extensions/Extmongodb.ini  rb  ^&         human/en/Extensions/Extoci8.ini  rb  9         human/en/Extensions/Extssh2.ini  rb  +x      !   human/en/Extensions/Extxmlrpc.inil  rbl  :          human/en/Extensions/Extstats.ini  rb  !      !   human/en/Extensions/Extapache.ini  rb  `          human/en/Extensions/Extposix.ini  rb  ڝ\x         human/en/Extensions/Extffi.ini  rb  !d          human/en/Extensions/Extxxtea.inid  rbd  ~k      #   human/en/Extensions/Extfileinfo.inix  rbx  Zw          human/en/Extensions/Extgnupg.init  rbt  P         human/en/Extensions/Extnewt.ini  rb  xJF         human/en/Extensions/Extamqp.ini  rb  #rd         human/en/Extensions/Extmail.ini  rb  Z          human/en/Extensions/Extparle.inis  rbs  v#         human/en/Extensions/Extjson.iniz  rbz  ]T      '   human/en/Extensions/Exteaccelerator.iniR  rbR  ݒ      "   human/en/Extensions/Extenchant.ini  rb  y      !   human/en/Extensions/Exttrader.ini  rb        "   human/en/Extensions/Extimagick.ini  rb  /^L          human/en/Extensions/Extcairo.ini  rb  H          human/en/Extensions/Extctype.ini  rb  BF         human/en/Extensions/Extsdl.ini  rb   1          human/en/Extensions/Exticonv.ini-  rb-  u=          human/en/Extensions/Extredis.iniZ  rbZ  ~          human/en/Extensions/Extshmop.iniq  rbq  1NM         human/en/Extensions/Extwddx.ini[  rb[  đ         human/en/Extensions/Extgrpc.inia  rba  -Z         human/en/Extensions/Extereg.ini  rb  8G         human/en/Extensions/Extfile.ini  rb  !_         human/en/Extensions/Exttidy.iniT  rbT  Q{̨      #   human/en/Extensions/Extcalendar.ini  rb  7      !   human/en/Extensions/Extexpect.ini^  rb^        !   human/en/Extensions/Extcrypto.iniK  rbK  ij6      #   human/en/Extensions/Extzbarcode.ini  rb  iL         human/en/Extensions/Extsvm.ini|  rb|            human/en/Extensions/Extmssql.ini/  rb/  뤛Ԥ      "   human/en/Extensions/Extsqlite3.ini  rb  p      "   human/en/Extensions/Extinotify.ini  rb  c9         human/en/Extensions/Extming.iniq  rbq  Ҥ         human/en/Extensions/Extzip.ini  rb  {vǤ         human/en/Extensions/Extuuid.ini  rb  Ҥ         human/en/Extensions/Extdb2.iniI  rbI  &         human/en/Extensions/Extapcu.ini"  rb"  t8(z      $   human/en/Extensions/Extxmlreader.inim  rbm  CxF         human/en/Extensions/Extpdo.iniE  rbE  cU      !   human/en/Extensions/Extmcrypt.ini|  rb|  fؤ      !   human/en/Extensions/Extbcmath.iniC  rbC           human/en/Extensions/Exthttp.ini  rb  |         human/en/Extensions/Extpcov.inip  rbp  ]j         human/en/Extensions/Extkdm5.iniV  rbV  I(zQ         human/en/Extensions/Extjudy.ini'  rb'  فrA         human/en/Extensions/Extdba.ini  rb  gc       "   human/en/Extensions/Extmsgpack.ini!  rb!        "   human/en/Extensions/Extgettext.ini  rb  coW         human/en/Extensions/Extlua.ini  rb  w)H         human/en/Extensions/Extpsr.ini/  rb/  si1Ĥ      !   human/en/Extensions/Extlibxml.iniQ  rbQ  S         human/en/Extensions/Extxsl.inif  rbf  Ñ      %   human/en/Extensions/Extopencensus.ini  rb  0         human/en/Extensions/Extintl.ini  rb  ]c~      #   human/en/Extensions/Extpassword.ini  rb  
         human/en/Extensions/Extfam.ini  rb  凤      "   human/en/Extensions/Extopenssl.iniK  rbK  N         human/en/Extensions/Extapc.ini  rb           human/en/Extensions/Extcurl.ini  rb  i8          human/en/Extensions/Extpgsql.ini_  rb_  bM      &   human/en/Namespaces/EmptyNamespace.ini  rb  Τ      6   human/en/Namespaces/MultipleAliasDefinitionPerFile.ini)  rb)  Dä      '   human/en/Namespaces/ShouldMakeAlias.ini  rb  	0v      '   human/en/Namespaces/Namespacesnames.ini  rb        %   human/en/Namespaces/CouldUseAlias.inis  rbs  *0      .   human/en/Namespaces/ConstantFullyQualified.ini  rb  lQ      /   human/en/Namespaces/ConstantWithUseFavorite.ini  rb  -B         human/en/Namespaces/Alias.ini  rb  ̿I      $   human/en/Namespaces/GlobalImport.ini  rb  {      !   human/en/Namespaces/UnusedUse.ini  rb  L      !   human/en/Namespaces/HiddenUse.ini  rb  𭓤      -   human/en/Namespaces/UseFunctionsConstants.ini-  rb-  W      0   human/en/Namespaces/MultipleAliasDefinitions.ini\  rb\  =      %   human/en/Namespaces/UnresolvedUse.ini  rb  ;      &   human/en/Namespaces/AliasConfusion.ini	  rb	  dB>      /   human/en/Namespaces/UseWithFullyQualifiedNS.ini  rb  UN      !   human/en/Namespaces/WrongCase.iniI  rbI  3ݯL         human/en/Namespaces/UsedUse.ini  rb  3p$      &   human/en/Namespaces/NamespaceUsage.ini   rb            human/en/Project/IsLibrary.ini   rb   C      (   human/en/Attributes/NestedAttributes.ini"  rb"        1   human/en/Attributes/MissingAttributeAttribute.ini  rb  %灤      '   human/en/Attributes/ModifyImmutable.ini  rb  @#         human/en/Type/Nowdoc.ini  rb  Nڤ          human/en/Type/MalformedOctal.ini  rb  Op         human/en/Type/ArrayIndex.ini  rb  j9         human/en/Type/UdpDomains.ini  rb  Ҥ      !   human/en/Type/SimilarIntegers.ini  rb  0L*      %   human/en/Type/StringHoldAVariable.ini  rb  Ӭ_E         human/en/Type/GPCIndex.inim  rbm  {8      (   human/en/Type/StringWithStrangeSpace.ini  rb  r<b         human/en/Type/Heredoc.ini  rb  w         human/en/Type/Pcre.ini  rb  xM          human/en/Type/Shellcommands.ini  rb        %   human/en/Type/StringInterpolation.ini  rb  tj_Ϥ         human/en/Type/UnicodeBlock.inij  rbj  x<         human/en/Type/OctalInString.ini  rb  W/      %   human/en/Type/ShouldBeSingleQuote.iniu  rbu  6         human/en/Type/Protocols.ini   rb   w{         human/en/Type/Ports.ini  rb  ey7         human/en/Type/Pack.ini  rb  `         human/en/Type/Binary.ini  rb  FL6         human/en/Type/Continents.ini   rb   (?      "   human/en/Type/DuplicateLiteral.ini  rb  oz         human/en/Type/Md5String.ini  rb  6nw         human/en/Type/Url.ini  rb  Ó         human/en/Type/Hexadecimal.ini  rb  ݤ         human/en/Type/Printf.ini  rb  ֹ9         human/en/Type/Path.ini,  rb,  9S          human/en/Type/ShouldTypecast.ini  rb            human/en/Type/CharString.ini  rb  w      !   human/en/Type/SpecialIntegers.ini  rb  Dh      $   human/en/Type/OneVariableStrings.ini  rb           human/en/Type/Sql.ini  rb           human/en/Type/Sapi.inig  rbg  h{         human/en/Type/MimeType.ini(  rb(  kp         human/en/Type/Regex.ini  rb  3      #   human/en/Type/HexadecimalString.inim  rbm  \F)         human/en/Type/HttpStatus.iniY  rbY        %   human/en/Type/SilentlyCastInteger.ini  rb  qp      "   human/en/Type/NoRealComparison.ini 	  rb 	  S:         human/en/Type/Email.iniX  rbX  z;Ѥ         human/en/Type/HttpHeader.ini:  rb:  ۤ         human/en/Type/OpensslCipher.inii  rbi  Ю\         human/en/Type/Octal.ini  rb  ĉߤ         human/en/Type/Integer.iniT  rbT  Nqդ      "   human/en/Variables/CloseNaming.ini  rb  d*      $   human/en/Variables/RealVariables.ini  rb  Vʱ2      "   human/en/Variables/VariablePhp.ini  rb  V"      &   human/en/Variables/StaticVariables.ini  rb  "[      (   human/en/Variables/UndefinedVariable.ini  rb        *   human/en/Variables/WrittenOnlyVariable.ini  rb  ,㜬      "   human/en/Variables/UniqueUsage.ini  rb  R8         human/en/Variables/Globals.ini  rb  ܿS      *   human/en/Variables/ComplexDynamicNames.ini|  rb|  "^;,      "   human/en/Variables/StrangeName.ini  rb  C}H      %   human/en/Variables/UncommonEnvVar.ini6  rb6  >>s      (   human/en/Variables/VariableVariables.ini  rb  rF      #   human/en/Variables/ConstantTypo.ini  rb  "#      -   human/en/Variables/Php7IndirectExpression.ini  rb  T      *   human/en/Variables/OverwrittenLiterals.ini  rb  "\      #   human/en/Variables/VariableLong.ini6  rb6  J͜      $   human/en/Variables/SelfTransform.iniy  rby  ,Ǌ      -   human/en/Variables/Php5IndirectExpression.iniO  rbO  ;8d0      !   human/en/Variables/References.ini  rb  @B      #   human/en/Variables/LocalGlobals.ini  rb  q;      '   human/en/Variables/VariableUsedOnce.ini  rb  G<         human/en/Variables/Blind.ini  rb  B      '   human/en/Variables/VariableNonascii.ini  rb        0   human/en/Variables/VariableUsedOnceByContext.ini  rb  &b      (   human/en/Variables/VariableOneLetter.ini  rb  Z;      ,   human/en/Variables/UndefinedConstantName.ini0  rb0        *   human/en/Variables/NoStaticVarInMethod.ini1  rb1  	ɤ      "   human/en/Variables/Overwriting.iniQ  rbQ  #O      .   human/en/Variables/InheritedStaticVariable.ini  rb  t{e      (   human/en/Variables/InconsistentUsage.iniq  rbq  TޗC      %   human/en/Variables/LostReferences.ini  rb  eC      &   human/en/Variables/IsLocalConstant.ini   rb   :j@      *   human/en/Variables/AssignedTwiceOrMore.ini  rb  )      (   human/en/Variables/RecycledVariables.ini  rb  JVp      (   human/en/Variables/VariableUppercase.ini  rb        )   human/en/Variables/InterfaceArguments.ini  rb  cA;         human/en/Arrays/NullBoolean.ini  rb  ؤ         human/en/Arrays/Arrayindex.ini+  rb+  ~         human/en/Arrays/MixedKeys.ini  rb  M:         human/en/Arrays/SliceFirst.ini'  rb'  1      *   human/en/Arrays/FloatConversionAsIndex.ini  rb  sp      !   human/en/Arrays/NegativeStart.ini  rb  ee      !   human/en/Arrays/AmbiguousKeys.ini  rb  ϯ      $   human/en/Arrays/Multidimensional.ini  rb  d      )   human/en/Arrays/MultipleIdenticalKeys.inih  rbh  Q*      )   human/en/Arrays/MistakenConcatenation.ini  rb  tpU          human/en/Arrays/ArrayNSUsage.iniB  rbB  K|       %   human/en/Arrays/TooManyDimensions.iniE  rbE  ʄ)      *   human/en/Arrays/RandomlySortedLiterals.ini
  rb
  li      +   human/en/Arrays/ArrayBracketConsistence.ini  rb  9          human/en/Arrays/MassCreation.ini  rb  Qx      &   human/en/Arrays/GettingLastElement.iniN  rbN  )¤      #   human/en/Arrays/NoSpreadForHash.ini  rb  s|I         human/en/Arrays/WeirdIndex.ini  rb  3?         human/en/Arrays/EmptyFinal.iniY  rbY  a      $   human/en/Arrays/NonConstantArray.ini  rb  ߧv      !   human/en/Arrays/Phparrayindex.ini4  rb4  d`nW      $   human/en/Arrays/ShouldPreprocess.ini  rb  ٻ         human/en/Arrays/EmptySlots.ini  rb  {      (   human/en/Arrays/StringInitialization.ini  rb  }          human/en/Arrays/WithCallback.ini  rb  @      %   human/en/Files/NotDefinitionsOnly.ini7  rb7  XOB      "   human/en/Files/DefinitionsOnly.ini  rb  ML         human/en/Files/IsComponent.inig  rbg  ^ѱ      !   human/en/Files/GlobalCodeOnly.ini   rb   K      !   human/en/Files/MissingInclude.ini  rb  *         human/en/Files/Services.ini  rb  #e          human/en/Files/IsCliScript.ini   rb   O
=      %   human/en/Files/InclusionWrongCase.ini  rb  Ҥ      '   human/en/Portability/LinuxOnlyFiles.ini  rb  u      '   human/en/Portability/GlobBraceUsage.iniv  rbv  I$      "   human/en/Portability/FopenMode.ini  rb  Dy      &   human/en/Portability/IconvTranslit.iniP  rbP  T-      -   human/en/Portability/WindowsOnlyConstants.ini  rb  `E      )   human/en/Functions/OneLetterFunctions.ini  rb  SU들      )   human/en/Functions/ShouldUseConstants.ini  rb  %      &   human/en/Functions/DeepDefinitions.ini
  rb
  /t
      '   human/en/Functions/NoReferencedVoid.ini  rb  (7ͤ      &   human/en/Functions/TooMuchIndented.ini
  rb
  lV      '   human/en/Functions/NullTypeFavorite.ini  rb        '   human/en/Functions/FallbackFunction.iniK  rbK  Wh      .   human/en/Functions/FunctionsUsingReference.ini-  rb-  Ng5      (   human/en/Functions/UnbindingClosures.ini  rb  ~      )   human/en/Functions/NoBooleanAsDefault.ini  rb  3R      #   human/en/Functions/NoReturnUsed.ini0  rb0  a      "   human/en/Functions/LoopCalling.ini	  rb	  麤      +   human/en/Functions/TypehintedReferences.ini  rb  X      )   human/en/Functions/HasFluentInterface.inid  rbd  䱸      &   human/en/Functions/MissingTypehint.iniG  rbG  VJ      )   human/en/Functions/DeprecatedCallable.ini	  rb	  oD      +   human/en/Functions/UsesDefaultArguments.ini1  rb1  ~      ,   human/en/Functions/TooManyLocalVariables.ini  rb  DΚ.      ,   human/en/Functions/NoLiteralForReference.ini  rb   b      )   human/en/Functions/ShouldYieldWithKey.iniv  rbv  d      /   human/en/Functions/UselessReferenceArgument.ini	  rb	  ްؤ         human/en/Functions/Closures.ini  rb        ,   human/en/Functions/HasNotFluentInterface.ini   rb   9S      .   human/en/Functions/UnusedInheritedVariable.ini}	  rb}	  iU䋤      #   human/en/Functions/MarkCallable.ini   rb   Ƥ         human/en/Functions/IsGlobal.ini   rb   W          human/en/Functions/Typehints.ini  rb  Lv      ,   human/en/Functions/MismatchParameterName.ini  rb  l      &   human/en/Functions/UselessArgument.ini	  rb	  (ꑤ      /   human/en/Functions/MismatchParameterAndType.iniE  rbE        )   human/en/Functions/ShouldBeTypehinted.ini  rb  קyp      #   human/en/Functions/PrefixToType.ini5  rb5        &   human/en/Functions/UnusedArguments.ini  rb        "   human/en/Functions/Dynamiccall.iniZ  rbZ  *I_      $   human/en/Functions/EmptyFunction.ini(  rb(  .v      )   human/en/Functions/CancelledParameter.inij  rbj  T      $   human/en/Functions/UsedFunctions.ini  rb  6,      -   human/en/Functions/MismatchTypeAndDefault.ini>  rb>  A      4   human/en/Functions/WrongNumberOfArgumentsMethods.ini  rb        +   human/en/Functions/NullableWithoutCheck.ini  rb  0      1   human/en/Functions/MismatchedDefaultArguments.ini  rb  N6      (   human/en/Functions/OptionalParameter.ini  rb  }b      4   human/en/Functions/OnlyVariablePassedByReference.ini  rb  a燤      )   human/en/Functions/CouldTypeWithArray.ini  rb  ?      $   human/en/Functions/RealFunctions.ini  rb  .      $   human/en/Functions/WithoutReturn.ini  rb  :       2   human/en/Functions/FnArgumentVariableConfusion.ini  rb  ?/      .   human/en/Functions/DuplicateNamedParameter.iniP  rbP  X      "   human/en/Functions/IsGenerator.ini?  rb?  H      +   human/en/Functions/InsufficientTypehint.ini  rb  ?M      "   human/en/Functions/DontUseVoid.ini  rb  ]f#      %   human/en/Functions/SemanticTyping.ini
  rb
  qA      '   human/en/Functions/UnsetOnArguments.inin  rbn  ~      +   human/en/Functions/ModifyTypedParameter.ini.  rb.  'S{         human/en/Functions/CantUse.ini  rb  i]      %   human/en/Functions/MultipleReturn.ini1  rb1  79%      -   human/en/Functions/TypehintMustBeReturned.ini  rb  2M      #   human/en/Functions/AliasesUsage.ini  rb  F'      *   human/en/Functions/CouldTypeWithString.ini  rb  VΤ      $   human/en/Functions/UselessReturn.ini(  rb(  Y      +   human/en/Functions/ConditionedFunctions.ini  rb  pf3      $   human/en/Functions/CouldTypehint.ini  rb  a      &   human/en/Functions/CouldBeCallable.ini
  rb
  xT      +   human/en/Functions/MultipleDeclarations.ini  rb  rɤ      '   human/en/Functions/UselessTypeCheck.ini  rb  4ޤ      &   human/en/Functions/UsingDeprecated.ini[  rb[  )L      ,   human/en/Functions/RedeclaredPhpFunction.iniq  rbq  8Z      *   human/en/Functions/WrongTypehintedName.ini  rb  ;xY         human/en/Functions/KillsApp.ini  rb  f"      &   human/en/Functions/ParameterHiding.ini<  rb<  XZ+      $   human/en/Functions/IsExtFunction.ini~  rb~  Ċ      /   human/en/Functions/OnlyVariableForReference.ini  rb  g      )   human/en/Functions/NeverUsedParameter.ini  rb  X      ,   human/en/Functions/MultipleSameArguments.ini/  rb/  #      &   human/en/Functions/UnusedFunctions.ini	  rb	  '      %   human/en/Functions/UselessDefault.ini
  rb
        -   human/en/Functions/WrongNumberOfArguments.ini=
  rb=
  :^      $   human/en/Functions/RelayFunction.ini  rb  >4      2   human/en/Functions/FunctionCalledWithOtherCase.ini  rb  {`\      -   human/en/Functions/WrongOptionalParameter.ini  rb  wZ      &   human/en/Functions/CouldCentralize.iniN  rbN  T      *   human/en/Functions/UnusedReturnedValue.iniS  rbS  [W]      /   human/en/Functions/MultipleIdenticalClosure.ini  rb  Lhܤ      0   human/en/Functions/CannotUseStaticForClosure.ini'  rb'  fb      (   human/en/Functions/NoClassAsTypehint.ini/  rb/  5OY      )   human/en/Functions/funcGetArgModified.inig  rbg  iC      '   human/en/Functions/CouldTypeWithInt.ini  rb  t      (   human/en/Functions/VariableArguments.ini  rb   6}      *   human/en/Functions/CallbackNeedsReturn.iniu  rbu  7=      &   human/en/Functions/AddDefaultValue.iniQ  rbQ  4zs          human/en/Functions/Recursive.inia  rba  ic      !   human/en/Functions/MustReturn.ini  rb        ,   human/en/Functions/UseConstantsAsReturns.inir  rbr  O12      (   human/en/Functions/WrongTypeWithCall.ini  rb  ~          human/en/Functions/WrongCase.ini  rb  [_      )   human/en/Functions/HardcodedPasswords.inis  rbs  B      +   human/en/Functions/CouldBeStaticClosure.ini&  rb&  kԤ      +   human/en/Functions/UnknownParameterName.ini  rb  SL      +   human/en/Functions/NullableWithConstant.ini  rb  	      (   human/en/Functions/CouldTypeWithBool.ini  rb  ^      +   human/en/Functions/AvoidBooleanArgument.ini  rb  n      "   human/en/Functions/DynamicCode.ini  rb  }.      7   human/en/Functions/WrongArgumentNameWithPhpFunction.ini  rb  EYʤ      '   human/en/Functions/BadTypehintRelay.iniy  rby  "      (   human/en/Functions/TooManyParameters.ini  rb  z      (   human/en/Functions/WrongReturnedType.ini  rb  mǤ      )   human/en/Functions/MismatchedTypehint.iniI  rbI  w      (   human/en/Functions/ExceedingTypehint.inia  rba        $   human/en/Functions/Functionnames.iniF  rbF  X`8      )   human/en/Functions/UndefinedFunctions.ini  rb  4ߤ      (   human/en/Functions/WrongArgumentType.ini  rb  @|      %   human/en/Functions/Closure2String.ini  rb        (   human/en/Functions/UseArrowFunctions.ini^  rb^  . ڤ      ,   human/en/Functions/GeneratorCannotReturn.ini  rb  &Qo      -   human/en/Functions/UseConstantAsArguments.ini  rb  \~      ,   human/en/Functions/CouldTypeWithIterable.ini  rb           human/en/Psr/Psr3Usage.iniW  rbW  ]P         human/en/Psr/Psr16Usage.ini  rb  C         human/en/Psr/Psr11Usage.ini  rb  S]         human/en/Psr/Psr7Usage.ini  rb  O|         human/en/Psr/Psr6Usage.ini  rb  xgi         human/en/Psr/Psr13Usage.ini  rb  lA      (   human/en/Rulesets/CompatibilityPHP72.ini   rb   h{F      !   human/en/Rulesets/Preferences.ini  rb  ~      (   human/en/Rulesets/CompatibilityPHP73.ini   rb   .(      (   human/en/Rulesets/CompatibilityPHP71.ini   rb   5:̤          human/en/Rulesets/Deprecated.ini   rb            human/en/Rulesets/First.ini  rb  e]      (   human/en/Rulesets/CompatibilityPHP70.ini   rb   0Ϥ      (   human/en/Rulesets/CompatibilityPHP74.ini   rb   n۳         human/en/Rulesets/Security.ini   rb   6         human/en/Rulesets/CE.ini   rb   
Ԥ          human/en/Rulesets/Typechecks.ini   rb   ~&         human/en/Rulesets/Appinfo.ini   rb   ud         human/en/Rulesets/Semantics.ini   rb   6<      )   human/en/Rulesets/Php-recommendations.ini0  rb0        '   human/en/Rulesets/Classdependencies.ini   rb         "   human/en/Rulesets/Performances.ini   rb   ]ۮn      "   human/en/Rulesets/php-cs-fixer.ini  rb  ɻ(         human/en/Rulesets/Rector.iniE  rbE  W6e      (   human/en/Rulesets/Coding Conventions.ini  rb  XD      !   human/en/Rulesets/ClassReview.ini   rb   #c         human/en/Rulesets/CI-checks.ini   rb   c}0         human/en/Rulesets/All.ini   rb   1q]      %   human/en/Rulesets/LintButWontExec.iniM  rbM  ä         human/en/Rulesets/Dump.ini2  rb2  6      (   human/en/Rulesets/CompatibilityPHP53.ini   rb   .i          human/en/Rulesets/Attributes.ini   rb   ;/      !   human/en/Rulesets/Suggestions.iniI  rbI  ܷ         human/en/Rulesets/Analyze.ini   rb   %~         human/en/Rulesets/Inventory.ini4  rb4  vL      (   human/en/Rulesets/CompatibilityPHP55.ini   rb   Y      (   human/en/Rulesets/CompatibilityPHP82.ini   rb   齤         human/en/Rulesets/Top10.ini   rb         (   human/en/Rulesets/CompatibilityPHP54.ini   rb   h*N      (   human/en/Rulesets/CompatibilityPHP56.ini   rb   ג      (   human/en/Rulesets/CompatibilityPHP81.ini   rb   5         human/en/Rulesets/Dead code.ini   rb   p      (   human/en/Rulesets/CompatibilityPHP80.ini   rb    u      $   human/en/Typehints/CouldBeParent.iniI  rbI         &   human/en/Typehints/CouldBeIterable.ini  rb  BD      #   human/en/Typehints/CouldNotType.ini  rb  qg      "   human/en/Typehints/CouldBeVoid.ini  rb  Ȥ      !   human/en/Typehints/CouldBeInt.ini  rb  V      "   human/en/Typehints/CouldBeNull.iniX  rbX  !<      $   human/en/Typehints/CouldBeString.ini  rb  (|      (   human/en/Typehints/MissingReturntype.ini  rb  ?j      &   human/en/Typehints/CouldBeCallable.ini  rb  
U      #   human/en/Typehints/CouldBeNever.iniu  rbu  XSִ      "   human/en/Typehints/CouldBeSelf.ini  rb        %   human/en/Typehints/CouldBeBoolean.ini  rb  ٤      '   human/en/Typehints/CouldBeGenerator.ini  rb  /      "   human/en/Typehints/CouldBeType.ini  rb  OHR      !   human/en/Typehints/CouldBeCIT.ini  rb  aѤ      #   human/en/Typehints/CouldBeArray.ini6  rb6   C]      '   human/en/Typehints/MissingTypehints.ini	  rb	  r`g      #   human/en/Typehints/CouldBeFloat.ini  rb  bHI         human/en/Custom/.gitignoreF   rbF   @         human/en/DSL/Back.inix   rbx   !*+         human/en/DSL/CodeIsNot.ini   rb   a&W         human/en/DSL/CountBy.ini^   rb^   4n7x         human/en/DSL/FollowCalls.ini   rb   >l         human/en/DSL/HasNoClass.ini   rb   {[         human/en/DSL/AddETo.ini   rb   ]b!         human/en/DSL/GoToAllRight.ini  rb  ?z         human/en/DSL/Not.ini  rb  X         human/en/DSL/NoQuery.ini   rb   .         human/en/DSL/IsInCatchBlock.inis   rbs   Pa         human/en/DSL/SavePropertyAs.inis   rbs   Q      "   human/en/DSL/CollectContainers.ini|   rb|   4         human/en/DSL/FullcodeIsNot.ini  rb  F          human/en/DSL/StopQuery.ini   rb   \|         human/en/DSL/FollowValue.ini5  rb5  x         human/en/DSL/InterfaceLike.ini   rb         "   human/en/DSL/NoClassDefinition.ini|   rb|   ]S         human/en/DSL/IsNotLowercase.inis   rbs   03         human/en/DSL/HasNoFunction.inip   rbp   iՕ      -   human/en/DSL/NoAnalyzerInsideWithProperty.ini   rb   4         human/en/DSL/GoToImplements.inis   rbs   /_         human/en/DSL/InIsNot.inix   rbx   . Τ         human/en/DSL/IsNotIgnored.ini   rb   0v֤         human/en/DSL/IsNotHash.inid   rbd   i Y      !   human/en/DSL/CollectTypehints.ini  rb  v;         human/en/DSL/IsLess.ini[   rb[   *          human/en/DSL/HasNoClassTrait.ini   rb   Ϥy         human/en/DSL/NoCodeInside.inim   rbm    Ӥ      !   human/en/DSL/GoToCurrentScope.iniy   rby   *<]g         human/en/DSL/IsNullable.ini   rb            human/en/DSL/HasInterface.ini   rb   1=         human/en/DSL/RegexIsNot.ini   rb   6         human/en/DSL/InitVariable.inim   rbm   )         human/en/DSL/HasNoChildren.inip   rbp   Ftv         human/en/DSL/NextSiblings.inim   rbm   #]$>         human/en/DSL/Range.ini   rb   nL      #   human/en/DSL/AtomInsideWithCall.ini   rb   ܁{         human/en/DSL/SetProperty.inij   rbj   ʲ}ܤ         human/en/DSL/HasNoIn.ini   rb            human/en/DSL/AtomInside.ini   rb            human/en/DSL/HasClass.ini   rb   bޤ         human/en/DSL/GoToExpression.inis   rbs   @|         human/en/DSL/GoToTrait.inid   rbd   `c      #   human/en/DSL/AtomInsideMoreThan.ini   rb   54
         human/en/DSL/HasNoCatch.ini   rb   5         human/en/DSL/Select.ini  rb  ?./      !   human/en/DSL/SaveMethodNameAs.iniy   rby   %      "   human/en/DSL/HasClassInterface.ini|   rb|   g         human/en/DSL/SameTypehintAs.ini   rb   ֛       '   human/en/DSL/AtomInsideNoDefinition.ini  rb  ۉ         human/en/DSL/PropertyIs.inig   rbg   \c      !   human/en/DSL/HasNoNextSibling.iniy   rby   m;]	         human/en/DSL/AnalyzerInside.ini   rb            human/en/DSL/GoToClassTrait.ini   rb   2=         human/en/DSL/HasIfthen.ini   rb   3|H         human/en/DSL/_As.ini   rb   ~B         human/en/DSL/HasNoParent.inij   rbj   S      '   human/en/DSL/AnalyzerInsideMoreThan.ini   rb   O      %   human/en/DSL/IsReferencedArgument.ini   rb   PXb      #   human/en/DSL/VariableIsAssigned.ini   rb   E<      !   human/en/DSL/CollectArguments.iniu  rbu  7         human/en/DSL/NoAtomInside.ini   rb         #   human/en/DSL/GoToClassInterface.ini   rb   4F:      "   human/en/DSL/CheckTypeWithAtom.ini  rb  _|          human/en/DSL/IsMissingOrNull.ini   rb   4N?      &   human/en/DSL/AtomInsideNoAnonymous.ini   rb   V         human/en/DSL/Filter.ini[   rb[            human/en/DSL/Count.iniX   rbX   ]T         human/en/DSL/IsArgument.ini   rb   z         human/en/DSL/NextSibling.inij   rbj   $2       %   human/en/DSL/GoToAllParentsTraits.ini   rb   1      '   human/en/DSL/IsNotExtendingComposer.ini   rb   PkE         human/en/DSL/HasAtomInside.inip   rbp   te         human/en/DSL/Command.ini^   rb^   1	         human/en/DSL/Implementing.inim   rbm   Jm          human/en/DSL/NoChildWithRank.ini   rb   5K      !   human/en/DSL/MakeVariableName.iniy   rby   o         human/en/DSL/DSLFactory.inig   rbg   ~ʦ         human/en/DSL/IsNot.ini   rb   X         human/en/DSL/GetVariable.ini'  rb'  Q      !   human/en/DSL/NoFunctionInside.iniy   rby   qK         human/en/DSL/CollectMethods.iniV  rbV  bP      "   human/en/DSL/NoTraitDefinition.ini|   rb|   Զc         human/en/DSL/IsReassigned.inim   rbm   *         human/en/DSL/GoToAllParents.inis   rbs   d9!      !   human/en/DSL/NoDelimiterIsNot.ini   rb   w?         human/en/DSL/GoToAllTraits.inip   rbp   uS          human/en/DSL/GoToTraits.inig   rbg            human/en/DSL/GetNameInFNP.inim   rbm   (ы         human/en/DSL/IsThis.ini   rb            human/en/DSL/GoToAllElse.inij   rbj   =9          human/en/DSL/GetStringLength.iniv   rbv   o/&         human/en/DSL/AtomIsNot.ini}   rb}   El      $   human/en/DSL/InterfaceDefinition.ini   rb   )         human/en/DSL/Extending.inid   rbd            human/en/DSL/FullcodeIs.inig   rbg   R6          human/en/DSL/HasNoComparison.iniv   rbv   "         human/en/DSL/IsMore.ini[   rb[   uϤ      %   human/en/DSL/AtomInsideExpression.ini   rb   _ J         human/en/DSL/Optional.ini  rb  XI         human/en/DSL/ReturnCount.inij   rbj            human/en/DSL/GoToNamespace.inip   rbp   Б         human/en/DSL/GoToLoop.ini   rb            human/en/DSL/HasInstruction.inis   rbs   kz      $   human/en/DSL/SamePropertyAsArray.ini   rb   c         human/en/DSL/IsUppercase.inij   rbj   %         human/en/DSL/ProcessLevels.ini   rb   Ӥ      #   human/en/DSL/OutWithoutLastRank.ini   rb   ¤         human/en/DSL/TokenIsNot.ini   rb   }[         human/en/DSL/OtherSiblings.inip   rbp            human/en/DSL/Property.inia   rba   5      "   human/en/DSL/FunctioncallIsNot.ini|   rb|   =	d         human/en/DSL/FullcodeInside.inis   rbs   6w&`         human/en/DSL/IsVisible.php   rb   y         human/en/DSL/IsEqual.ini^   rb^   ?         human/en/DSL/Is.inil   rbl   Ӷ8         human/en/DSL/HasTryCatch.ini   rb   I      "   human/en/DSL/NotSamePropertyAs.ini|   rb|   >4          human/en/DSL/HasNoDefinition.iniv   rbv   }Wgw      (   human/en/DSL/GoToClassInterfaceTrait.ini   rb   iZ¤         human/en/DSL/AnalyzerIs.ini   rb   -W         human/en/DSL/HasChildren.inij   rbj   VS      (   human/en/DSL/HasNoConstantDefinition.ini   rb   c      !   human/en/DSL/HasNoInstruction.iniy   rby   1I         human/en/DSL/IsNotLiteral.inim   rbm   IT         human/en/DSL/FunctioncallIs.inis   rbs   ʗ          human/en/DSL/PreviousSibling.iniv   rbv   H         human/en/DSL/OutIs.inia   rba   e          human/en/DSL/NoUseDefinition.iniv   rbv   ؤ         human/en/DSL/InIsIE.inix   rbx         $   human/en/DSL/HasNoClassInterface.ini   rb   T|%      !   human/en/DSL/NoFullcodeInside.iniy   rby   cV         human/en/DSL/HasNoIfthen.ini   rb   *         human/en/DSL/FullnspathIs.inim   rbm   8xI      $   human/en/DSL/IsComplexExpression.ini   rb   m         human/en/DSL/PreviousCalls.ini  rb  ԋ         human/en/DSL/HasParent.inid   rbd   7T         human/en/DSL/HasLoop.ini   rb   A      $   human/en/DSL/HasVariadicArgument.ini   rb   ¤      &   human/en/DSL/CodeIsPositiveInteger.ini   rb            human/en/DSL/IsVisible.ini   rb   &         human/en/DSL/FullcodeLength.inis   rbs   e:         human/en/DSL/CollectTraits.ini   rb   *9ä         human/en/DSL/IsNotUppercase.inis   rbs   7Ҥ         human/en/DSL/GoToInterface.inip   rbp   56         human/en/DSL/CodeIs.ini}   rb}         %   human/en/DSL/IsNotInheritedMethod.ini   rb   5O?         human/en/DSL/PropertyIsNot.inip   rbp   '         human/en/DSL/OutIsIE.ini   rb   
JФ         human/en/DSL/Trim.ini  rb  Nr<2      (   human/en/DSL/ProcessIndentingAverage.ini   rb   {      #   human/en/DSL/HasTraitDefinition.ini   rb   -̤         human/en/DSL/CodeLength.ini   rb   >3      !   human/en/DSL/FollowExpression.iniy   rby   Nv      "   human/en/DSL/IsPropertyDefined.ini   rb   wϤ         human/en/DSL/HasNoInterface.ini   rb   %toA         human/en/DSL/FetchContext.inim   rbm   ;         human/en/DSL/GoToArray.inid   rbd   $9         human/en/DSL/HasClassTrait.inip   rbp   *         human/en/DSL/SaveOutAs.inid   rbd   ;ޤ         human/en/DSL/NotExtending.inim   rbm   bTte         human/en/DSL/AddEFrom.ini   rb   NjҤ      #   human/en/DSL/GoToParameterUsage.ini   rb   |         human/en/DSL/Unique.ini[   rb[         !   human/en/DSL/PreviousSiblings.iniy   rby   OB         human/en/DSL/IsNotNullable.ini   rb            human/en/DSL/GroupCount.inig   rbg   4gW         human/en/DSL/FunctionInside.inis   rbs   k\      !   human/en/DSL/CollectVariables.iniy   rby   t	      "   human/en/DSL/CollectImplements.ini|   rb|   K<B      "   human/en/DSL/HasPropertyInside.ini|   rb|   S      &   human/en/DSL/HasFunctionDefinition.ini   rb   B         human/en/DSL/AtomIs.init   rbt   8Al         human/en/DSL/HasNoUsage.inig   rbg   J){          human/en/DSL/GoToAllChildren.iniv   rbv   V      "   human/en/DSL/NotSameTypehintAs.ini   rb   lwʤ         human/en/DSL/IsHash.ini[   rb[   r      !   human/en/DSL/GoToLiteralValue.iniy   rby   B      &   human/en/DSL/HasNoVariadicArgument.ini   rb   9-3         human/en/DSL/HasNoLoop.ini   rb   j48         human/en/DSL/Raw.ini   rb   sI^      #   human/en/DSL/FunctionDefinition.ini   rb   ^         human/en/DSL/RegexIs.inil   rbl            human/en/DSL/HasFunction.inij   rbj   M         human/en/DSL/GroupFilter.inij   rbj   e         human/en/DSL/NoDelimiterIs.ini   rb   @$         human/en/DSL/AnalyzerIsNot.ini   rb   Ҥ         human/en/DSL/IsNotMixedcase.inis   rbs   f&Ƥ         human/en/DSL/Side.iniU   rbU   ?J         human/en/DSL/Dedup.iniX   rbX   mb         human/en/DSL/HasIn.ini   rb            human/en/DSL/FollowAlias.ini  rb  z =      &   human/en/DSL/HasNoNamedInstruction.ini   rb         &   human/en/DSL/NoInterfaceDefinition.ini   rb   Q[w         human/en/DSL/Values.ini[   rb[   #e      %   human/en/DSL/IsNotPropertyDefined.ini   rb   yߤ         human/en/DSL/HasNoOut.ini   rb   p      ,   human/en/DSL/NoAtomWithoutPropertyInside.ini   rb   
2         human/en/DSL/IsGlobalCode.inim   rbm   =?       !   human/en/DSL/NoAnalyzerInside.iniy   rby   yۤ      %   human/en/DSL/NoAtomPropertyInside.ini   rb            human/en/DSL/IsNotEmptyBody.inis   rbs   hDeb         human/en/DSL/TokenIs.iniz   rbz   5ͤ      (   human/en/DSL/GoToParameterDefinition.ini&  rb&  NФ      '   human/en/DSL/HasInterfaceDefinition.ini   rb   	D顤         human/en/DSL/OutIsNot.ini   rb   ,l[         human/en/DSL/GoToParent.inig   rbg   x\@         human/en/DSL/InIs.inik   rbk   1e         human/en/DSL/IsLocalClass.inim   rbm   a      )   human/en/DSL/HasNoClassInterfaceTrait.ini   rb   U      #   human/en/DSL/HasClassDefinition.ini   rb   a         human/en/DSL/IsUsed.ini[   rb[   <      $   human/en/DSL/CountArrayDimension.ini   rb   K         human/en/DSL/OutWithRank.ini   rb   FФ         human/en/DSL/HasNextSibling.inis   rbs   `W&      &   human/en/DSL/HasConstantDefinition.ini   rb   Fä      !   human/en/DSL/HasChildWithRank.iniy   rby   \b         human/en/DSL/GoToExtends.inij   rbj   p&ۤ         human/en/DSL/Has.ini~   rb~   Ns7          human/en/DSL/ClassDefinition.ini   rb   	gWm         human/en/DSL/GoToFunction.inim   rbm   wk=ʤ      &   human/en/DSL/NotCompatibleWithType.ini!  rb!  OK         human/en/DSL/HasNo.iniX   rbX   dMb         human/en/DSL/GoToClass.inid   rbd   LX         human/en/DSL/HasTrait.ini|   rb|   BO[Y         human/en/DSL/HasNoTryCatch.ini   rb   x_         human/en/DSL/SamePropertyAs.inis   rbs   Y          human/en/DSL/IsNotLocalClass.iniv   rbv   Wa         human/en/DSL/CollectExtends.inis   rbs             human/en/DSL/HasOut.ini   rb   , 1I          human/en/DSL/FullnspathIsNot.iniv   rbv   (s      "   human/en/DSL/AtomInsideNoBlock.ini   rb   =         human/en/DSL/AtomFunctionIs.ini   rb   F          human/en/DSL/IsNotEmptyArray.iniv   rbv   &         human/en/DSL/IsLowercase.inij   rbj   ^;4          human/en/DSL/GoToInstruction.iniv   rbv   ]F      $   human/en/DSL/GoToFirstExpression.ini   rb   
,`         human/en/DSL/FollowParAs.ini  rb  C^      (   human/en/DSL/HasNoFunctionDefinition.ini   rb   [|         human/en/DSL/VariableIsRead.inis   rbs   ƬǤ      "   human/en/DSL/GoToAllImplements.ini|   rb|   K          human/en/DSL/NotImplementing.iniv   rbv   w         human/en/DSL/IsNotArgument.ini   rb   р      (   human/en/DSL/HasNoCountedInstruction.ini   rb            human/en/DSL/IsLiteral.iniv   rbv   PG      #   human/en/DSL/FullcodeVariableIs.ini   rb         %   human/en/DSL/ProcessDereferencing.ini   rb   yr         human/en/DSL/HasNoTrait.ini   rb   p         human/en/DSL/Ignore.ini[   rb[   k7o         human/en/DSL/GoToFile.ini   rb   p       &   human/en/Interfaces/UsedInterfaces.ini  rb        (   human/en/Interfaces/IsNotImplemented.ini}  rb}  h      (   human/en/Interfaces/UnusedInterfaces.ini  rb  cYӤ         human/en/Interfaces/Php.ini  rb  YǸ      *   human/en/Interfaces/PossibleInterfaces.ini  rb  ͤ      0   human/en/Interfaces/CantImplementTraversable.ini  rb  u7      &   human/en/Interfaces/Interfacenames.ini  rb  PԤ      '   human/en/Interfaces/InterfaceMethod.inip  rbp  Qd)      )   human/en/Interfaces/UselessInterfaces.ini  rb  z{h      5   human/en/Interfaces/NoGaranteeForPropertyConstant.ini  rb  Et      +   human/en/Interfaces/UndefinedInterfaces.ini(  rb(  ٬      )   human/en/Interfaces/CouldUseInterface.ini  rb  A$D      ,   human/en/Interfaces/AvoidSelfInInterface.ini]  rb]  Mˤ      &   human/en/Interfaces/IsExtInterface.ini  rb  :p1      &   human/en/Interfaces/InterfaceUsage.ini  rb  ^      *   human/en/Interfaces/ConcreteVisibility.ini;  rb;  6~      )   human/en/Interfaces/RepeatedInterface.ini+  rb+  ԡ      0   human/en/Interfaces/NoConstructorInInterface.ini  rb  8'      -   human/en/Interfaces/CantOverloadConstants.ini  rb  ,ԸM      &   human/en/Interfaces/EmptyInterface.ini_  rb_  ET      /   human/en/Interfaces/AlreadyParentsInterface.ini  rb  nwޤ          human/en/Reports/RadwellCode.ini  rb  菤      #   human/en/Reports/PhpCompilation.ini  rb  U      #   human/en/Reports/Clustergrammer.ini  rb  -         human/en/Reports/Text.ini  rb  !      '   human/en/Reports/Compatibilityphp74.iniz  rbz  j1o         human/en/Reports/.DS_Store  rb  j m         human/en/Reports/None.ini\  rb\  9g         human/en/Reports/Uml.ini^  rb^  ݋         human/en/Reports/Stubs.ini  rb  k0K         human/en/Reports/TypeChecks.ini  rb  =@      #   human/en/Reports/TypeSuggestion.ini&  rb&  g˫          human/en/Reports/Migration74.ini  rb  􍢤      %   human/en/Reports/PhpConfiguration.ini  rb  %~v         human/en/Reports/Yaml.iniC  rbC  Z      !   human/en/Reports/PublicAccess.iniI  rbI  }         human/en/Reports/StubsJson.ini  rb  7         human/en/Reports/Emissary.ini  rb  hI      )   human/en/Reports/Filedependencieshtml.ini  rb  T<         human/en/Reports/Stats.ini  rb  ޤ         human/en/Reports/Composer.ini>  rb>  4X.         human/en/Reports/Ambassador.inih  rbh  F         human/en/Reports/History.ini  rb  1J      &   human/en/Reports/Classdependencies.ini  rb  K         human/en/Reports/Exakatjson.ini~  rb~  e         human/en/Reports/Rector.ini>  rb>        %   human/en/Reports/Filedependencies.ini-  rb-  <         human/en/Reports/Plantuml.ini  rb  |>Ť         human/en/Reports/Diplomat.ini  rb  *_Τ         human/en/Reports/Unused.iniH  rbH  h          human/en/Reports/Migration82.ini  rb  N'o         human/en/Reports/Xml.iniW  rbW  [ͤ         human/en/Reports/Marmelab.iniD  rbD  W          human/en/Reports/Migration81.ini  rb  A@         human/en/Reports/Perrule.ini  rb  X|         human/en/Reports/Phpcsfixer.ini	  rb	  .D          human/en/Reports/ClassReview.ini  rb  7l          human/en/Reports/Migration80.ini  rb  ޥ'         human/en/Reports/Owasp.ini  rb  K         human/en/Reports/Meters.ini  rb  aIo         human/en/Reports/Sarif.ini  rb           human/en/Reports/Sarb.ini  rb  Y          human/en/Reports/BeautyCanon.ini  rb  dw          human/en/Reports/SimpleTable.ini  rb  V          human/en/Reports/CodeSniffer.ini  rb  <      $   human/en/Reports/DependencyWheel.ini  rb  ud         human/en/Reports/Json.iniE  rbE  C         human/en/Reports/Inventory.inii  rbi  T         human/en/Reports/Perfile.ini  rb  z         human/en/Reports/CodeFlower.ini  rb  Ñxn         human/en/Reports/Exakatyaml.ini  rb  I*         human/en/Reports/Top10.iniM  rbM  s         human/en/Reports/Topology.ini  rb  M      '   human/en/Reports/Compatibilityphp56.iniz  rbz  }Q      '   human/en/Reports/Compatibilityphp81.iniz  rbz  Af         human/en/Reports/Phpcity.ini  rb  ~I      '   human/en/Reports/Compatibilityphp80.iniz  rbz  _Sk      '   server/gsneo4j/gsneo4j/gsneo4j.3.2.yaml  rb  #q6u      (   server/gsneo4j/gsneo4j/exakat.properties  rb  ]      '   server/gsneo4j/gsneo4j/gsneo4j.3.3.yaml  rb  5      '   server/gsneo4j/gsneo4j/gsneo4j.3.4.yaml  rb  5      (   server/gsneo4j/gsneo4j/gremlin-server.sh  rb  9s         server/gsneo4j/gsneo4j.3.2.yaml  rb  #q6u          server/gsneo4j/exakat.properties  rb  ]         server/gsneo4j/gsneo4j.3.3.yaml  rb  5         server/gsneo4j/gsneo4j.3.4.yaml  rb  5          server/gsneo4j/gremlin-server.sh  rb  9s         server/lint_short_tags.php  rb  u_K         server/exakat.ini  rb  &O         server/.DS_Store(  rb(  Ӹ      #   server/gsneo4jv3/gsneo4jv3.3.5.yaml^
  rb^
  ܶo         server/gsneo4jv3/.DS_Store  rb  j m      "   server/gsneo4jv3/exakat.properties  rb  ]      #   server/gsneo4jv3/gsneo4jv3.3.4.yaml^
  rb^
  ܶo      -   server/gsneo4jv3/gsneo4jv3/gsneo4jv3.3.5.yaml^
  rb^
  ܶo      $   server/gsneo4jv3/gsneo4jv3/.DS_Store  rb  j m      ,   server/gsneo4jv3/gsneo4jv3/exakat.properties  rb  ]      -   server/gsneo4jv3/gsneo4jv3/gsneo4jv3.3.4.yaml^
  rb^
  ܶo      ,   server/gsneo4jv3/gsneo4jv3/gremlin-server.sh  rb  9s      "   server/gsneo4jv3/gremlin-server.sh  rb  9s         server/tinkergraphv3/.DS_Store  rb  j m      ,   server/tinkergraphv3/tinkergraphv3/.DS_Store  rb  j m      9   server/tinkergraphv3/tinkergraphv3/tinkergraphv3.3.4.yamlL
  rbL
  v      4   server/tinkergraphv3/tinkergraphv3/gremlin-server.sh  rb  9s      +   server/tinkergraphv3/tinkergraphv3.3.4.yamlL
  rbL
  v      &   server/tinkergraphv3/gremlin-server.sh  rb  9s      '   server/tinkergraph/tinkergraph.3.3.yamlK
  rbK
  P         server/tinkergraph/.DS_Store  rb  j m      '   server/tinkergraph/tinkergraph.3.2.yaml  rb  `      3   server/tinkergraph/tinkergraph/tinkergraph.3.3.yamlK
  rbK
  P      (   server/tinkergraph/tinkergraph/.DS_Store  rb  j m      3   server/tinkergraph/tinkergraph/tinkergraph.3.2.yaml  rb  `      3   server/tinkergraph/tinkergraph/tinkergraph.3.4.yamlK
  rbK
  P      0   server/tinkergraph/tinkergraph/gremlin-server.sh  rb  9s      '   server/tinkergraph/tinkergraph.3.4.yamlK
  rbK
  P      $   server/tinkergraph/gremlin-server.sh  rb  9s         server/http/index.php  rb  rS         server/http/server.sh   rb   R0:         server/lint.php  rb  "         vendor/autoload.php  rb  M6(         vendor/.DS_Store  rb  O         vendor/bin/yaml-lint  rb  9          vendor/bin/.phpunit.result.cache   rb   \ׅ      K   vendor/guiguiboy/php-cli-progress-bar/test/ProgressBar/Test/ManagerTest.php  rb  Ϧͤ      L   vendor/guiguiboy/php-cli-progress-bar/test/ProgressBar/Test/RegistryTest.php  rb  #      6   vendor/guiguiboy/php-cli-progress-bar/test/AllTest.phpN  rbN  8      >   vendor/guiguiboy/php-cli-progress-bar/ProgressBar/Registry.php  rb  @V=Ť      =   vendor/guiguiboy/php-cli-progress-bar/ProgressBar/Manager.php  rb        /   vendor/guiguiboy/php-cli-progress-bar/README.md  rb  Nw      0   vendor/guiguiboy/php-cli-progress-bar/.gitignore8   rb8   ,ȗ      4   vendor/guiguiboy/php-cli-progress-bar/.git/ORIG_HEAD)   rb)   [      1   vendor/guiguiboy/php-cli-progress-bar/.git/config   rb   M      i   vendor/guiguiboy/php-cli-progress-bar/.git/objects/pack/pack-f59e7c3192719a6566ac81d0b8f1502fe4e44048.idx  rb  !A      j   vendor/guiguiboy/php-cli-progress-bar/.git/objects/pack/pack-f59e7c3192719a6566ac81d0b8f1502fe4e44048.packO  rbO  e      =   vendor/guiguiboy/php-cli-progress-bar/.git/objects/info/packs6   rb6   UzF#      /   vendor/guiguiboy/php-cli-progress-bar/.git/HEAD)   rb)   [      7   vendor/guiguiboy/php-cli-progress-bar/.git/info/exclude   rb   w=!      4   vendor/guiguiboy/php-cli-progress-bar/.git/info/refs   rb   y?      4   vendor/guiguiboy/php-cli-progress-bar/.git/logs/HEAD  rb  >3      A   vendor/guiguiboy/php-cli-progress-bar/.git/logs/refs/heads/master   rb   3~      L   vendor/guiguiboy/php-cli-progress-bar/.git/logs/refs/remotes/composer/master   rb   Q      H   vendor/guiguiboy/php-cli-progress-bar/.git/logs/refs/remotes/origin/HEAD   rb   3~      6   vendor/guiguiboy/php-cli-progress-bar/.git/descriptionI   rbI   7      B   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/commit-msg.sample  rb        B   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/pre-rebase.sampleW  rbW  ,.      B   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/pre-commit.samplej  rbj  %0\      F   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/applypatch-msg.sample  rb  O	      C   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/pre-receive.sample   rb         J   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/prepare-commit-msg.sample  rb        C   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/post-update.sample   rb         F   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/pre-applypatch.sample  rb  L      @   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/pre-push.sampleD  rbD  ؏      >   vendor/guiguiboy/php-cli-progress-bar/.git/hooks/update.sample  rb  !D%      <   vendor/guiguiboy/php-cli-progress-bar/.git/refs/heads/master)   rb)   [      G   vendor/guiguiboy/php-cli-progress-bar/.git/refs/remotes/composer/master)   rb)   [      C   vendor/guiguiboy/php-cli-progress-bar/.git/refs/remotes/origin/HEAD    rb    %Ԡ      0   vendor/guiguiboy/php-cli-progress-bar/.git/index{  rb{  Mt5      6   vendor/guiguiboy/php-cli-progress-bar/.git/packed-refsk   rbk   ʯ      5   vendor/guiguiboy/php-cli-progress-bar/.git/FETCH_HEAD|   rb|   V+p      3   vendor/guiguiboy/php-cli-progress-bar/composer.json.  rb.  %
      '   vendor/composer/autoload_namespaces.php   rb   RϤ         vendor/composer/LICENSE.  rb.         K   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/ChangeLog-8.1.md  rb  ?.je      F   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/phpunit.xml  rb  >+      B   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/LICENSE  rb  4&       K   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/ChangeLog-8.0.md)  rb)  O      D   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/build.xmlY  rbY  ;r      J   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/README.md  rb  蜤      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BeforeClassWithOnlyDataProviderTest.phpQ  rbQ  JL      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BankAccountTest2.php  rb  ˤ      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AssertionExample.phpd  rbd  J$<?      P   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/3194.php  rb  ^h      Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/3530.wsdl  rb  D      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassThatImplementsSerializable.phpA  rbA  D      P   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/Book.phpw  rbw         \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AnotherInterface.phpC  rbC  3      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithAllPossibleReturnTypes.php}  rb}  Ł|      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithScalarTypeDeclarations.phpk  rbk  $      h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithNonPublicAttributes.php  rb  <*Τ      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BankAccountTest.test.php&  rb&  :'"      m   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ChangeCurrentWorkingDirectoryTest.php  rb  0      [   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ConcreteTest.my.php  rb  zb      W   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AnInterface.php:  rb:  <^B      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AbstractTest.php  rb        h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BeforeClassAndAfterClassTest.php  rb  {ڤ      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AssertionExampleTest.php  rb  K⛤      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AbstractMockTestClass.php  rb  2ۤ      [   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ArrayAccessible.php  rb  @       ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithToString.php  rb  PaT      V   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/Calculator.php  rb        k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithVariadicArgumentMethod.php  rb  h(MM      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithStaticMethod.phpY  rbY  ;&ɤ      Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AbstractTrait.php  rb  V?9f      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BeforeAndAfterTest.phpU  rbU  >      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ConcreteTest.phpz  rbz  k      [   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BankAccountTest.php  rb  #yҙ      W   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/BankAccount.php'  rb'  2=      R   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/Author.php  rb  a/      O   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/Bar.phpV  rbV  %%      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/ClassWithSelfTypeHint.phpR  rbR  `      e   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/tests/_files/AnInterfaceWithReturnType.phpQ  rbQ  =      M   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.phpstorm.meta.phpV  rbV  E(      N   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.psalm/baseline.xml5T  rb5T  qǤ      L   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.psalm/config.xmlC  rbC  Z	8ͤ      U   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.psalm/static-analysis.xml  rb  "y(      H   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.editorconfig   rb   V       D   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/README.md
  rb
  @      B   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/phpunit  rb  9      F   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/phpunit.xsdB  rbB  D^ޤ      E   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.gitignorew  rbw  v      N   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.github/FUNDING.yml;   rb;   pN
      U   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.github/CODE_OF_CONDUCT.mdZ	  rbZ	  uI1      T   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.github/ISSUE_TEMPLATE.mde  rbe        R   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.github/CONTRIBUTING.md	
  rb	
  01      I   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.gitattributes;   rb;   (      D   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/phive.xml?  rb?   <*      K   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/ChangeLog-8.2.mdp
  rbp
  
      F   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.travis.yml   rb   2 m      H   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/composer.json3	  rb3	  'K      G   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/.php_cs.dist   rb   Di      K   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/ChangeLog-7.5.md)  rb)  y      W   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/TextUI/ResultPrinter.php.;  rb.;  쥤      T   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/TextUI/TestRunner.php  rb  ?xؤ      N   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/TextUI/Help.phpo0  rbo0  ~,      Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/TextUI/Command.php  rb  )      S   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/TextUI/Exception.php  rb  ۤ      Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/RegularExpression.php?  rb?  Qqg      L   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Type.php  rb  z#M      L   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Json.php"
  rb"
  .y      R   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/FileLoader.php-	  rb-	  z      R   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Filesystem.php  rb  ~F      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/ConfigurationGenerator.phpe  rbe         Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Blacklist.php  rb  F%      N   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Getopt.php  rb  g      K   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Xml.php !  rb !  xӤ      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/InvalidArgumentHelper.php  rb  f      U   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Configuration.phpC  rbC  =      V   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/eval-stdin.php-  rb-  -rX      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/WindowsPhpProcess.phpt  rbt  2K      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/AbstractPhpProcess.php(  rb(  5      f   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/Template/PhptTestCase.tpl.distb  rbb  ̤      g   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/Template/TestCaseClass.tpl.dist  rb  3/Q      h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/Template/TestCaseMethod.tpl.distW  rbW  i      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/PHP/DefaultPhpProcess.php  rb  f¤      L   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Test.phpR  rbR        S   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/GlobalState.phpA  rbA  Ȥ      M   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Color.php  rb  ROh.      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/ResultPrinter.php  rb  Hw      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/HtmlResultPrinter.php
  rb
  w      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/TextResultPrinter.php$  rb$  zi>      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/XmlResultPrinter.php  rb  뾪      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/CliTestDoxPrinter.php<*  rb<*  ӥ      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/NamePrettifier.php  rb  .D      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TestDox/TestDoxPrinter.php)  rb)  ub      N   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Filter.phph
  rbh
  O       [   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/XmlTestListRenderer.php%
  rb%
  `B      T   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Log/TeamCity.php(  rb(  A2      Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Log/JUnit.php=+  rb=+  UT      T   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/ErrorHandler.php3  rb3  DsҤ      Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Exception.php  rb  #!>      O   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/Printer.php  rb  SD      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/XdebugFilterScriptGenerator.php  rb  >C      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Util/TextTestListRenderer.php@  rb@  7      Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestListener.php  rb  `O&      W   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestResult.phpHu  rbHu  S      n   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestListenerDefaultImplementation.php  rb  T]Ꙥ      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestSuiteIterator.phpF  rbF  "      V   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestSuite.phpS  rbS  I      [   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/SelfDescribing.php  rb  F      [   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/IncompleteTest.php  rb  #9d      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestBuilder.php  rb        ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/ExceptionWrapper.php:  rb:  \F      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Assert/Functions.php rb 1`      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/DataProviderTestSuite.php  rb  <S      S   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Assert.phpi rbi -'      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/SkippedTest.php  rb  )ً      Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Test.php  rb  8      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockClass.phpy  rby  6      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockObject.php  rb  <_      h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/InvocationMocker.phpR  rbR  1      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockType.php  rb  '5      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Invokable.php  rb        b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Invocation.phpG  rbG  W69      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockBuilder.php  rb        e   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockMethodSet.php  rb  <)      j   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/MethodName.php)  rb)  FE      u   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/ConsecutiveParameters.php  rb  fr      m   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/DeferredError.php  rb  t      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/InvokedRecorder.php  rb  Z      j   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/Invocation.php  rb  Jۤ      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/AnyInvokedCount.phpW  rbW  Ǉ      r   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/InvokedAtLeastOnce.php:  rb:  =q      s   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/StatelessInvocation.php  rb  QO      m   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/AnyParameters.php  rb  GO      r   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/InvokedAtMostCount.php  rb  ֥Z      n   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/InvokedAtIndex.php  rb  K]      s   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/InvokedAtLeastCount.php  rb  52      j   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/Parameters.php&  rb&  Ѥ      l   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher/InvokedCount.php
  rb
  k      t   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/mocked_method.tpl.distL  rbL  V      u   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/proxied_method.tpl.dist  rb  a      r   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/deprecation.tpl.dist;   rb;   O5s      q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/wsdl_class.tpl.dist   rb         z   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/mocked_class_method.tpl.dist   rb   d      y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/mocked_method_void.tpl.dist  rb  *      r   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/trait_class.tpl.distQ   rbQ   <Ȥ      u   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/unmocked_clone.tpl.dist   rb   8W}ؤ      r   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/wsdl_method.tpl.dist<   rb<   i      s   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/mocked_clone.tpl.dist   rb   aT      z   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/proxied_method_void.tpl.dist|  rb|  |򦺤      s   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/mocked_class.tpl.distj  rbj  8      {   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator/mocked_static_method.tpl.dist   rb    4R      j   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/ConfigurableMethod.php  rb        _   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Matcher.php!  rb!        k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/ConfigurableMethods.php  rb  mä      g   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ReturnStub.php  rb        m   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ConsecutiveCalls.php  rb  aؤ      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ReturnArgument.php  rb        l   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ReturnReference.php  rb        n   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/MatcherCollection.php  rb  5*      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ReturnValueMap.php  rb  'ؤ      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ReturnCallback.php  rb  񳲤      g   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/ReturnSelf.php7  rb7  n      f   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub/Exception.php#  rb#  D	C;      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Stub.php   rb   U      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockMethod.php/  rb/  [w      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Generator.phpQ}  rbQ}        n   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/NamespaceMatch.php  rb  ё      p   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/InvocationMocker.php  rb  2k^      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/ParametersMatch.php  rb        h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/Identity.php  rb  }"      e   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/Match.php  rb  E      d   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/Stub.php  rb  JT      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Builder/MethodNameMatch.phpC  rbC  }k         vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Exception/ConfigurableMethodsAlreadyInitializedException.php   rb   }-      r   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Exception/RuntimeException.php  rb  "bB         vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Exception/IncompatibleReturnValueException.php  rb  t      x   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Exception/BadMethodCallException.php  rb  i      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Exception/Exception.php  rb  "      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/Verifiable.php  rb  /v      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/MockObject/MockTrait.php  rb  l      Z   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Error/Warning.php  rb  Jߤ      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Error/Error.php  rb  ;      Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Error/Notice.php  rb  
H      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Error/Deprecated.php  rb  ʼW      U   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestCase.php rb 8      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/Warning.phpc  rbc   J      v   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/UnintentionallyCoveredCodeError.php  rb  it      q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/ExpectationFailedException.php  rb  ݘ      l   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/SyntheticSkippedError.php  rb  ӏ      l   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/SkippedTestSuiteError.php  rb  f      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/OutputError.php  rb  :t      j   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/IncompleteTestError.php  rb  )R      e   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/RiskyTestError.php  rb  9d      g   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/SkippedTestError.php  rb  =@      e   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/SyntheticError.php&  rb&  i ٤      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/PHPTAssertionFailedError.php   rb   >G      l   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/CodeCoverageException.php  rb  <߆]      v   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/CoveredCodeNotExecutedException.php  rb  p      s   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/InvalidDataProviderException.php  rb  R      s   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/InvalidCoversTargetException.php  rb  U      w   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/MissingCoversAnnotationException.php  rb  1Ȥ      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/Exception.phpD	  rbD	  C4]      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Exception/AssertionFailedError.phpj  rbj  ZY      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/WarningTestCase.phpE  rbE  $d      i   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/RegularExpression.php5  rb5  FgC      d   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsInstanceOf.php  rb  	]8      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsReadable.phpd  rbd        _   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsFalse.php1  rb1        `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/LessThan.php  rb  
4      y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ExceptionMessageRegularExpression.php]  rb]         g   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/DirectoryExists.phpj  rbj  ޤ      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/LogicalOr.php
  rb
  "0l      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsAnything.php  rb  t      h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ExceptionMessage.php-  rb-  L8      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/TraversableContainsOnly.php  rb  {(/      o   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ClassHasStaticAttribute.php  rb  ?~      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsTrue.php-  rb-  2Đ      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/LogicalAnd.phpY  rbY  2/      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ArrayHasKey.php  rb  ~y      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/Composite.phpN  rbN  7      h   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/StringStartsWith.php  rb  ,JI      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsJson.php   rb   g      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/JsonMatches.php:  rb:  \F      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/Attribute.php  rb        b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/LogicalXor.php@  rb@  3n      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsType.php  rb  K      _   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsEmpty.phpG  rbG  'Y      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/TraversableContains.php`  rb`  \m      _   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsEqual.php  rb  zΤ      v   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/StringMatchesFormatDescription.phpe
  rbe
  n G      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsNan.php*  rb*  _hۤ      i   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ClassHasAttribute.phpl  rbl  ='      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/Constraint.php  rb  o      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/Count.php  rb  XL      j   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ObjectHasAttribute.phpb  rbb  t`      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsInfinite.php>  rb>        f   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/StringContains.php  rb  N0T      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsIdentical.php  rb  㯳c      w   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/JsonMatchesErrorMessageProvider.php@  rb@        ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsNull.php-  rb-  ɭw      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ArraySubset.php  rb  k      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/SameSize.php  rb  i*      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/Exception.php  rb  h_      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsWritable.phpd  rbd  _P      e   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/ExceptionCode.php;  rb;        `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/Callback.php  rb        b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/LogicalNot.php  rb  ?ʤ      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/GreaterThan.php  rb  Ǥ      f   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/StringEndsWith.phpO  rbO  
      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/IsFinite.php6  rb6  {0jR      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/Constraint/FileExists.php[  rb[  zEPe      _   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/IncompleteTestCase.php  rb  )HM      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/TestFailure.php  rb  9      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Framework/SkippedTestCase.php  rb        Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/TestResultCache.php  rb  Y)ɤ      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/BaseTestRunner.phpP  rbP  !O      ^   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/ResultCacheExtension.php*  rb*        V   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/PhptTestCase.phpHQ  rbHQ  ss0      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/StandardTestSuiteLoader.php  rb  
%,5      Q   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Version.php  rb  hlf      Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/TestSuiteLoader.php8  rb8  +$ˤ      `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/DefaultTestResultCache.php   rb   aƖ      Y   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/TestSuiteSorter.php-  rb-  kj      X   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Filter/Factory.phpQ  rbQ  W      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Filter/IncludeGroupFilterIterator.phpA  rbA  a      d   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Filter/GroupFilterIterator.php  rb  /      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Filter/NameFilterIterator.phpl  rbl  ;
h      k   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Filter/ExcludeGroupFilterIterator.phpB  rbB  ċ      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/NullTestResultCache.phpv  rbv  r      S   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Exception.php  rb  m]      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterRiskyTestHook.php  rb  jYM      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterSkippedTestHook.php  rb  J      a   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterTestErrorHook.php  rb  LXǤ      S   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/Hook.php+  rb+  kXM      ]   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/BeforeTestHook.php  rb  zݤ      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/TestListenerAdapter.php  rb  >P      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterTestWarningHook.php  rb  ֆ      W   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/TestHook.php<  rb<  xᤸ      \   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterTestHook.phpQ  rbQ        `   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterLastTestHook.phpw  rbw  ~F      c   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterTestFailureHook.php  rb  zc      b   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/BeforeFirstTestHook.php{  rb{  .      f   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterIncompleteTestHook.php  rb  *K      f   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Runner/Hook/AfterSuccessfulTestHook.php  rb  j3      L   vendor/composer/c8e9bd88/sebastianbergmann-phpunit-c1b8534/src/Exception.php  rb  楷         vendor/composer/ClassLoader.php>  rb>  5Ky      !   vendor/composer/autoload_psr4.php(  rb(  !ٗs      %   vendor/composer/autoload_classmap.php   rb   L      "   vendor/composer/platform_check.php  rb  ˤ      #   vendor/composer/autoload_static.php	  rb	  6B       !   vendor/composer/autoload_real.php  rb  '         vendor/composer/installed.json6  rb6  2,      "   vendor/composer/autoload_files.phpL  rbL  p      %   vendor/composer/InstalledVersions.php:  rb:  &l         vendor/composer/installed.php  rb  h         vendor/psy/.DS_Store   rb   HA         vendor/bartlett/.DS_Store   rb   䧤      (   vendor/bartlett/sarif-php-sdk/mkdocs.yml  rb   \      %   vendor/bartlett/sarif-php-sdk/LICENSE0  rb0  @a7      *   vendor/bartlett/sarif-php-sdk/CHANGELOG.md  rb  &'~      5   vendor/bartlett/sarif-php-sdk/docs/graph-composer.svgV  rbV  ".$      5   vendor/bartlett/sarif-php-sdk/docs/getting-started.mde  rbe  }Ȥ      +   vendor/bartlett/sarif-php-sdk/docs/index.md  rb  VG      D   vendor/bartlett/sarif-php-sdk/docs/reference/runAutomationDetails.md  rb  kԤ      5   vendor/bartlett/sarif-php-sdk/docs/reference/stack.md[  rb[  m*      3   vendor/bartlett/sarif-php-sdk/docs/reference/fix.md  rb  QZŤ      B   vendor/bartlett/sarif-php-sdk/docs/reference/externalProperties.mdv  rbv  /)m      :   vendor/bartlett/sarif-php-sdk/docs/reference/stackFrame.md  rb  UI\      E   vendor/bartlett/sarif-php-sdk/docs/reference/configurationOverride.md  rb  &Z      N   vendor/bartlett/sarif-php-sdk/docs/reference/externalPropertyFileReferences.mdZ  rbZ   .      ;   vendor/bartlett/sarif-php-sdk/docs/reference/replacement.mds  rbs  xN      9   vendor/bartlett/sarif-php-sdk/docs/reference/rectangle.mdV  rbV  q      6   vendor/bartlett/sarif-php-sdk/docs/reference/result.md  rb  Nto      O   vendor/bartlett/sarif-php-sdk/docs/reference/reportingDescriptorRelationship.md  rb        :   vendor/bartlett/sarif-php-sdk/docs/reference/attachment.mdg
  rbg
  ^      =   vendor/bartlett/sarif-php-sdk/docs/reference/edgeTraversal.md  rb  O      8   vendor/bartlett/sarif-php-sdk/docs/reference/codeFlow.md  rb  2lE      4   vendor/bartlett/sarif-php-sdk/docs/reference/tool.mdf  rbf  4H      D   vendor/bartlett/sarif-php-sdk/docs/reference/locationRelationship.md  rb  X>|      :   vendor/bartlett/sarif-php-sdk/docs/reference/threadFlow.md  rb        >   vendor/bartlett/sarif-php-sdk/docs/reference/graphTraversal.md&  rb&  :hs      M   vendor/bartlett/sarif-php-sdk/docs/reference/externalPropertyFileReference.mdX  rbX  s9S      :   vendor/bartlett/sarif-php-sdk/docs/reference/webRequest.md  rb        4   vendor/bartlett/sarif-php-sdk/docs/reference/node.mdc  rbc  &      :   vendor/bartlett/sarif-php-sdk/docs/reference/invocation.mdb  rbb  o      E   vendor/bartlett/sarif-php-sdk/docs/reference/versionControlDetails.md  rb  	h      8   vendor/bartlett/sarif-php-sdk/docs/reference/artifact.mdI  rbI  !Ǥ      4   vendor/bartlett/sarif-php-sdk/docs/reference/edge.md:  rb:  vȤ      L   vendor/bartlett/sarif-php-sdk/docs/reference/reportingDescriptorReference.md!  rb!  ,YФ      D   vendor/bartlett/sarif-php-sdk/docs/reference/translationMetadata..md
  rb
  /C      C   vendor/bartlett/sarif-php-sdk/docs/reference/reportingDescriptor.md  rb  $ު      8   vendor/bartlett/sarif-php-sdk/docs/reference/sarifLog.mdd  rbd  ڤ      7   vendor/bartlett/sarif-php-sdk/docs/reference/address.mdA  rbA  A      ;   vendor/bartlett/sarif-php-sdk/docs/reference/suppression.md  rb  <k      9   vendor/bartlett/sarif-php-sdk/docs/reference/exception.md  rb  X%      3   vendor/bartlett/sarif-php-sdk/docs/reference/run.mdG  rbG  M      B   vendor/bartlett/sarif-php-sdk/docs/reference/threadFlowLocation.md  rb  Z      7   vendor/bartlett/sarif-php-sdk/docs/reference/message.md'  rb'        ?   vendor/bartlett/sarif-php-sdk/docs/reference/artifactContent.md  rb  m      @   vendor/bartlett/sarif-php-sdk/docs/reference/resultProvenance.md`  rb`  Re      F   vendor/bartlett/sarif-php-sdk/docs/reference/reportingConfiguration.md  rb  A2J      ;   vendor/bartlett/sarif-php-sdk/docs/reference/webResponse.md  rb  ۺ      >   vendor/bartlett/sarif-php-sdk/docs/reference/artifactChange.mdf  rbf  G!      ?   vendor/bartlett/sarif-php-sdk/docs/reference/logicalLocation.mdf	  rbf	  ͣ\      <   vendor/bartlett/sarif-php-sdk/docs/reference/notification.mdR  rbR        F   vendor/bartlett/sarif-php-sdk/docs/reference/toolComponentReference.md  rb  5      @   vendor/bartlett/sarif-php-sdk/docs/reference/physicalLocation.md~  rb~        @   vendor/bartlett/sarif-php-sdk/docs/reference/specialLocations.md
  rb
  sE      :   vendor/bartlett/sarif-php-sdk/docs/reference/conversion.md
  rb
  i      5   vendor/bartlett/sarif-php-sdk/docs/reference/graph.md  rb  BB      +   vendor/bartlett/sarif-php-sdk/.editorconfig\  rb\  LzR      '   vendor/bartlett/sarif-php-sdk/README.md  rb  ZtϤ      /   vendor/bartlett/sarif-php-sdk/examples/tool.php*  rb*  xh      5   vendor/bartlett/sarif-php-sdk/examples/conversion.php  rb  5       ?   vendor/bartlett/sarif-php-sdk/examples/runAutomationDetails.php  rb  l      ;   vendor/bartlett/sarif-php-sdk/examples/physicalLocation.php  rb  鬤      >   vendor/bartlett/sarif-php-sdk/examples/translationMetadata.php  rb  :      2   vendor/bartlett/sarif-php-sdk/examples/address.php  rb  >lB      @   vendor/bartlett/sarif-php-sdk/examples/configurationOverride.php  rb  ~iu      1   vendor/bartlett/sarif-php-sdk/examples/result.php  rb  x      6   vendor/bartlett/sarif-php-sdk/examples/suppression.php  rb  jhe      9   vendor/bartlett/sarif-php-sdk/examples/graphTraversal.php	  rb	  
      4   vendor/bartlett/sarif-php-sdk/examples/rectangle.php  rb   <&      @   vendor/bartlett/sarif-php-sdk/examples/message/embeddedLinks.php4  rb4  b
S      ?   vendor/bartlett/sarif-php-sdk/examples/message/stringLookup.phpC  rbC        <   vendor/bartlett/sarif-php-sdk/examples/message/formatted.php6  rb6  q	5%      <   vendor/bartlett/sarif-php-sdk/examples/message/plainText.php<  rb<  #      .   vendor/bartlett/sarif-php-sdk/examples/run.php  rb  &O      G   vendor/bartlett/sarif-php-sdk/examples/reportingDescriptorReference.php  rb  !7      J   vendor/bartlett/sarif-php-sdk/examples/reportingDescriptorRelationship.php  rb  ɤ      .   vendor/bartlett/sarif-php-sdk/examples/fix.php  rb         0   vendor/bartlett/sarif-php-sdk/examples/graph.php  rb  Rx#      3   vendor/bartlett/sarif-php-sdk/examples/codeFlow.php	  rb	  =      3   vendor/bartlett/sarif-php-sdk/examples/artifact.php  rb  n      5   vendor/bartlett/sarif-php-sdk/examples/webRequest.php6  rb6  ټ@      0   vendor/bartlett/sarif-php-sdk/examples/stack.phpp
  rbp
  E       A   vendor/bartlett/sarif-php-sdk/examples/reportingConfiguration.php  rb  ћ&~      @   vendor/bartlett/sarif-php-sdk/examples/versionControlDetails.phpx  rbx  sF      ;   vendor/bartlett/sarif-php-sdk/examples/resultProvenance.php  rb  `?      ?   vendor/bartlett/sarif-php-sdk/examples/locationRelationship.php  rb  ._      ;   vendor/bartlett/sarif-php-sdk/examples/specialLocations.php  rb  l~%      >   vendor/bartlett/sarif-php-sdk/examples/reportingDescriptor.php  rb  p0      5   vendor/bartlett/sarif-php-sdk/examples/attachment.php  rb  `]6      3   vendor/bartlett/sarif-php-sdk/examples/sarifLog.php  rb  
%      4   vendor/bartlett/sarif-php-sdk/examples/exception.php  rb  moe      I   vendor/bartlett/sarif-php-sdk/examples/externalPropertyFileReferences.php  rb  U      :   vendor/bartlett/sarif-php-sdk/examples/logicalLocation.php  rb  Y@      =   vendor/bartlett/sarif-php-sdk/examples/externalProperties.php	  rb	  6      <   vendor/bartlett/sarif-php-sdk/.github/workflows/gh-pages.yml  rb  &{      ?   vendor/bartlett/sarif-php-sdk/.github/workflows/mega-linter.ymlW
  rbW
  Fˤ      =   vendor/bartlett/sarif-php-sdk/.github/linters/.phpcs.xml.dist  rb  
      3   vendor/bartlett/sarif-php-sdk/.github/linters/.ecrc  rb  :`      ?   vendor/bartlett/sarif-php-sdk/.github/linters/phpstan.neon.dist  rb  Z&      7   vendor/bartlett/sarif-php-sdk/.github/linters/psalm.xml  rb  {h      .   vendor/bartlett/sarif-php-sdk/.mega-linter.yml=  rb=  
D      +   vendor/bartlett/sarif-php-sdk/composer.json  rb  'J      G   vendor/bartlett/sarif-php-sdk/src/Property/InlineExternalProperties.php  rb  87      <   vendor/bartlett/sarif-php-sdk/src/Property/Relationships.phpD  rbD  *av      :   vendor/bartlett/sarif-php-sdk/src/Property/AsOfTimeUtc.php1  rb1        6   vendor/bartlett/sarif-php-sdk/src/Property/Enabled.php  rb  Uz4      H   vendor/bartlett/sarif-php-sdk/src/Property/ReportingDescriptorTarget.php  rb  '      V   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceConversion.php  rb        4   vendor/bartlett/sarif-php-sdk/src/Property/Edges.php  rb  >b      T   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceRequests.phpB  rbB  n:U      3   vendor/bartlett/sarif-php-sdk/src/Property/Guid.php7  rb7  T4      <   vendor/bartlett/sarif-php-sdk/src/Property/DeletedRegion.phpU  rbU  D8      ?   vendor/bartlett/sarif-php-sdk/src/Property/ExitSignalNumber.php:  rb:  :%      ;   vendor/bartlett/sarif-php-sdk/src/Property/BaselineGuid.php  rb  hwo      6   vendor/bartlett/sarif-php-sdk/src/Property/Headers.php@  rb@  xJ      ;   vendor/bartlett/sarif-php-sdk/src/Property/SourceNodeId.php'  rb'        =   vendor/bartlett/sarif-php-sdk/src/Property/ImmutableState.php  rb  f       :   vendor/bartlett/sarif-php-sdk/src/Property/Annotations.php  rb  @      Q   vendor/bartlett/sarif-php-sdk/src/Property/NotificationConfigurationOverrides.phpU  rbU  r(      5   vendor/bartlett/sarif-php-sdk/src/Property/Stacks.php  rb        7   vendor/bartlett/sarif-php-sdk/src/Property/Contents.phpP  rbP  OG      3   vendor/bartlett/sarif-php-sdk/src/Property/Kind.php  rb  Ï%      B   vendor/bartlett/sarif-php-sdk/src/Property/AssociatedComponent.php  rb  &Ӥ      6   vendor/bartlett/sarif-php-sdk/src/Property/Machine.php  rb  ,<      7   vendor/bartlett/sarif-php-sdk/src/Property/Markdown.php  rb  ?&      D   vendor/bartlett/sarif-php-sdk/src/Property/FirstDetectionTimeUtc.phpw  rbw  .$֤      4   vendor/bartlett/sarif-php-sdk/src/Property/Rules.php-  rb-  }O      ;   vendor/bartlett/sarif-php-sdk/src/Property/InitialState.php  rb  0vO      7   vendor/bartlett/sarif-php-sdk/src/Property/Protocol.php  rb  P$R      7   vendor/bartlett/sarif-php-sdk/src/Property/Rendered.phpz  rbz  4T+      4   vendor/bartlett/sarif-php-sdk/src/Property/Roles.php  rb  ؤ      7   vendor/bartlett/sarif-php-sdk/src/Property/Policies.php  rb  ʹ0      9   vendor/bartlett/sarif-php-sdk/src/Property/Importance.php  rb  e      =   vendor/bartlett/sarif-php-sdk/src/Property/AssociatedRule.php  rb  AP      >   vendor/bartlett/sarif-php-sdk/src/Property/ArtifactContent.php  rb  ͙      @   vendor/bartlett/sarif-php-sdk/src/Property/ConversionSources.php.  rb.  7}      6   vendor/bartlett/sarif-php-sdk/src/Property/RunGuid.phpR  rbR  d      A   vendor/bartlett/sarif-php-sdk/src/Property/OriginalUriBaseIds.php   rb   ?/_      =   vendor/bartlett/sarif-php-sdk/src/Property/ExecutionOrder.php  rb  .      B   vendor/bartlett/sarif-php-sdk/src/Property/ExitCodeDescription.phpX  rbX  A      4   vendor/bartlett/sarif-php-sdk/src/Property/Index.php  rb  ˤ      7   vendor/bartlett/sarif-php-sdk/src/Property/MimeType.php	  rb	  'Q      =   vendor/bartlett/sarif-php-sdk/src/Property/SourceLanguage.php5  rb5  J3      6   vendor/bartlett/sarif-php-sdk/src/Property/Product.php  rb  L٤      I   vendor/bartlett/sarif-php-sdk/src/Property/ToolExecutionNotifications.phpz  rbz  #      C   vendor/bartlett/sarif-php-sdk/src/Property/LastDetectionRunGuid.php  rb  Z      9   vendor/bartlett/sarif-php-sdk/src/Property/EndTimeUtc.php  rb  Hhw      8   vendor/bartlett/sarif-php-sdk/src/Property/EndColumn.php	  rb	  	      9   vendor/bartlett/sarif-php-sdk/src/Property/CharLength.php  rb        3   vendor/bartlett/sarif-php-sdk/src/Property/Rule.phpn  rbn  2      >   vendor/bartlett/sarif-php-sdk/src/Property/RedactionTokens.php  rb  W@X      C   vendor/bartlett/sarif-php-sdk/src/Property/EnvironmentVariables.phps  rbs  ~      T   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferencePolicies.php$  rb$  7      6   vendor/bartlett/sarif-php-sdk/src/Property/Results.php  rb  @      :   vendor/bartlett/sarif-php-sdk/src/Property/CommandLine.php   rb   xҤ      A   vendor/bartlett/sarif-php-sdk/src/Property/WebResponseDetails.phpb  rbb  Dtޤ      9   vendor/bartlett/sarif-php-sdk/src/Property/Properties.phpW  rbW  vCQK      <   vendor/bartlett/sarif-php-sdk/src/Property/MessageString.php5  rb5  0s      U   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceAddresses.php.  rb.  Fع      U   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceResponses.phpL  rbL  A      8   vendor/bartlett/sarif-php-sdk/src/Property/ItemCount.php  rb  0bJ      9   vendor/bartlett/sarif-php-sdk/src/Property/CharOffset.php  rb  ~KB      5   vendor/bartlett/sarif-php-sdk/src/Property/Stdout.phpL  rbL  |      6   vendor/bartlett/sarif-php-sdk/src/Property/TimeUtc.php  rb  5      @   vendor/bartlett/sarif-php-sdk/src/Property/RelationshipKinds.php  rb  ^      @   vendor/bartlett/sarif-php-sdk/src/Property/RequestParameters.phpP  rbP  Ϧ!      4   vendor/bartlett/sarif-php-sdk/src/Property/Label.php!  rb!  ۤ      >   vendor/bartlett/sarif-php-sdk/src/Property/RelativeAddress.php3  rb3  9      >   vendor/bartlett/sarif-php-sdk/src/Property/OccurrenceCount.php  rb  e      ;   vendor/bartlett/sarif-php-sdk/src/Property/Suppressions.php  rb        @   vendor/bartlett/sarif-php-sdk/src/Property/AutomationDetails.php  rb        >   vendor/bartlett/sarif-php-sdk/src/Property/HostedViewerUri.phpG  rbG  t>      ?   vendor/bartlett/sarif-php-sdk/src/Property/RuntimeException.phpS  rbS  *k      <   vendor/bartlett/sarif-php-sdk/src/Property/RepositoryUri.php9  rb9  Zb      :   vendor/bartlett/sarif-php-sdk/src/Property/WebRequests.php  rb  .      <   vendor/bartlett/sarif-php-sdk/src/Property/DecoratedName.php.  rb.  |u      C   vendor/bartlett/sarif-php-sdk/src/Property/AnalysisToolLogFiles.phpN  rbN  9      5   vendor/bartlett/sarif-php-sdk/src/Property/Bottom.php  rb  &      8   vendor/bartlett/sarif-php-sdk/src/Property/Locations.php  rb  kɤ      V   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceProperties.php  rb  u      3   vendor/bartlett/sarif-php-sdk/src/Property/Body.php:  rb:  |q      5   vendor/bartlett/sarif-php-sdk/src/Property/RuleId.php  rb  j      8   vendor/bartlett/sarif-php-sdk/src/Property/CallStack.php  rb  NǤ      G   vendor/bartlett/sarif-php-sdk/src/Property/PhysicalArtifactLocation.php  rb  Ĥ      :   vendor/bartlett/sarif-php-sdk/src/Property/Attachments.php  rb        ;   vendor/bartlett/sarif-php-sdk/src/Property/StdoutStderr.phpv  rbv  \      C   vendor/bartlett/sarif-php-sdk/src/Property/LastDetectionTimeUtc.phpp  rbp        >   vendor/bartlett/sarif-php-sdk/src/Property/InvocationIndex.php  rb  w      ;   vendor/bartlett/sarif-php-sdk/src/Property/WebResponses.php  rb  $y      7   vendor/bartlett/sarif-php-sdk/src/Property/Encoding.php  rb  ⒤      =   vendor/bartlett/sarif-php-sdk/src/Property/AnalysisTarget.php  rb  拑      6   vendor/bartlett/sarif-php-sdk/src/Property/Account.php  rb  @      ;   vendor/bartlett/sarif-php-sdk/src/Property/ProductSuite.php'  rb'  FK      >   vendor/bartlett/sarif-php-sdk/src/Property/KindSuppression.php  rb  ˻y      ;   vendor/bartlett/sarif-php-sdk/src/Property/CodeLocation.php>  rb>  7      <   vendor/bartlett/sarif-php-sdk/src/Property/DeprecatedIds.php  rb  ;      E   vendor/bartlett/sarif-php-sdk/src/Property/ConfigurationAtRuntime.php  rb  m      @   vendor/bartlett/sarif-php-sdk/src/Property/WebRequestDetails.phpW  rbW  M~ˤ      5   vendor/bartlett/sarif-php-sdk/src/Property/Method.php  rb  ~ݤ      <   vendor/bartlett/sarif-php-sdk/src/Property/ResponseFiles.php  rb  zz      :   vendor/bartlett/sarif-php-sdk/src/Property/DownloadUri.php   rb   |      >   vendor/bartlett/sarif-php-sdk/src/Property/DefaultEncoding.php<  rb<  NQ      ?   vendor/bartlett/sarif-php-sdk/src/Property/ContentsArtifact.php^  rb^  t      7   vendor/bartlett/sarif-php-sdk/src/Property/Children.php  rb  3u¤      >   vendor/bartlett/sarif-php-sdk/src/Property/FullDescription.php  rb  |¤      ?   vendor/bartlett/sarif-php-sdk/src/Property/ExecutionTimeUtc.phpT  rbT  C      >   vendor/bartlett/sarif-php-sdk/src/Property/SemanticVersion.php<  rb<  D      U   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceArtifacts.php  rb        B   vendor/bartlett/sarif-php-sdk/src/Property/MessageStringNative.php  rb  ~B      9   vendor/bartlett/sarif-php-sdk/src/Property/Rectangles.php  rb  Wal      5   vendor/bartlett/sarif-php-sdk/src/Property/Target.php  rb  0      I   vendor/bartlett/sarif-php-sdk/src/Property/RuleConfigurationOverrides.php  rb  ä      D   vendor/bartlett/sarif-php-sdk/src/Property/FirstDetectionRunGuid.php  rb  w      J   vendor/bartlett/sarif-php-sdk/src/Property/ArtifactLocationAssociation.php  rb  HI      D   vendor/bartlett/sarif-php-sdk/src/Property/DottedQuadFileVersion.php  rb  ^yu      ;   vendor/bartlett/sarif-php-sdk/src/Property/ReasonPhrase.php'  rb'  R/ۤ      8   vendor/bartlett/sarif-php-sdk/src/Property/StartLine.php	  rb	   \      B   vendor/bartlett/sarif-php-sdk/src/Property/ThreadFlowLocations.php  rb  P︤      >   vendor/bartlett/sarif-php-sdk/src/Property/InsertedContent.php  rb  y      R   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceDriver.php  rb  Ť      5   vendor/bartlett/sarif-php-sdk/src/Property/Driver.php@  rb@  A      ;   vendor/bartlett/sarif-php-sdk/src/Property/TargetNodeId.php'  rb'  2X      =   vendor/bartlett/sarif-php-sdk/src/Property/TargetLocation.php  rb  %      ;   vendor/bartlett/sarif-php-sdk/src/Property/Fingerprints.phpI  rbI  
Ϥ      S   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceResults.php  rb  f4      <   vendor/bartlett/sarif-php-sdk/src/Property/KindException.php  rb        ?   vendor/bartlett/sarif-php-sdk/src/Property/WorkingDirectory.php  rb  1Q      7   vendor/bartlett/sarif-php-sdk/src/Property/ExitCode.php  rb   |_Ϥ      7   vendor/bartlett/sarif-php-sdk/src/Property/ThreadId.php  rb        E   vendor/bartlett/sarif-php-sdk/src/Property/ExternalizedProperties.php  rb  `Vu      4   vendor/bartlett/sarif-php-sdk/src/Property/State.php  rb        >   vendor/bartlett/sarif-php-sdk/src/Property/ArtifactChanges.php  rb  gE)      ?   vendor/bartlett/sarif-php-sdk/src/Property/ResultGraphIndex.php  rb  , 
      D   vendor/bartlett/sarif-php-sdk/src/Property/LocationRelationships.php   rb   Qܤ      2   vendor/bartlett/sarif-php-sdk/src/Property/Top.php  rb  ,E      ?   vendor/bartlett/sarif-php-sdk/src/Property/RelatedLocations.php  rb  r      :   vendor/bartlett/sarif-php-sdk/src/Property/AddressKind.php  rb  Zh?      _   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceThreadFlowLocations.php  rb  M      A   vendor/bartlett/sarif-php-sdk/src/Property/NoResponseReceived.phpS  rbS  "      3   vendor/bartlett/sarif-php-sdk/src/Property/Left.php  rb        =   vendor/bartlett/sarif-php-sdk/src/Property/ReleaseDateUtc.php5  rb5  `      4   vendor/bartlett/sarif-php-sdk/src/Property/Level.php  rb  0      :   vendor/bartlett/sarif-php-sdk/src/Property/ThreadFlows.php  rb  {~      5   vendor/bartlett/sarif-php-sdk/src/Property/Branch.php  rb  |      B   vendor/bartlett/sarif-php-sdk/src/Property/PartialFingerprints.php  rb  F/      <   vendor/bartlett/sarif-php-sdk/src/Property/BaselineState.php,  rb,  w.ؤ      1   vendor/bartlett/sarif-php-sdk/src/Property/Id.php  rb  :      5   vendor/bartlett/sarif-php-sdk/src/Property/Module.php  rb  2      T   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferencesInlined.php3  rb3  aB<      B   vendor/bartlett/sarif-php-sdk/src/Property/ExecutionSuccessful.phpR  rbR  Ek      =   vendor/bartlett/sarif-php-sdk/src/Property/RegionArtifact.php,  rb,  ]̥      @   vendor/bartlett/sarif-php-sdk/src/Property/StepOverEdgeCount.php  rb  Ԕ      ;   vendor/bartlett/sarif-php-sdk/src/Property/ToolPipeline.php  rb  Y      3   vendor/bartlett/sarif-php-sdk/src/Property/Help.php^  rb^  Tä      6   vendor/bartlett/sarif-php-sdk/src/Property/Version.php  rb  +Ľ      ;   vendor/bartlett/sarif-php-sdk/src/Property/Replacements.php  rb  w      ;   vendor/bartlett/sarif-php-sdk/src/Property/ParameterBag.phpV  rbV  k¤      <   vendor/bartlett/sarif-php-sdk/src/Property/RunAggregates.php  rb  d!X      ;   vendor/bartlett/sarif-php-sdk/src/Property/WorkItemUris.php  rb  Qr7b      ?   vendor/bartlett/sarif-php-sdk/src/Property/LogicalLocations.php"  rb"  _@Y      I   vendor/bartlett/sarif-php-sdk/src/Property/ProcessStartFailureMessage.php  rb  vA      3   vendor/bartlett/sarif-php-sdk/src/Property/Rank.php  rb  Ǥ      C   vendor/bartlett/sarif-php-sdk/src/Property/DefaultConfiguration.php  rb  (      9   vendor/bartlett/sarif-php-sdk/src/Property/IdLocation.php  rb  z,y      =   vendor/bartlett/sarif-php-sdk/src/Property/EdgeTraversals.php  rb  D#      <   vendor/bartlett/sarif-php-sdk/src/Property/Notifications.php  rb  ԥ;      9   vendor/bartlett/sarif-php-sdk/src/Property/FinalState.php  rb  脑4      5   vendor/bartlett/sarif-php-sdk/src/Property/Graphs.php  rb  (c      W   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceInvocations.phpE  rbE        R   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceGraphs.php  rb  |0p      6   vendor/bartlett/sarif-php-sdk/src/Property/HelpUri.php  rb  wNw      >   vendor/bartlett/sarif-php-sdk/src/Property/CorrelationGuid.php  rb  G[      ?   vendor/bartlett/sarif-php-sdk/src/Property/ToolComponentRef.php  rb  S{      5   vendor/bartlett/sarif-php-sdk/src/Property/Frames.php  rb  m      ?   vendor/bartlett/sarif-php-sdk/src/Property/ShortDescription.php  rb  ¤      :   vendor/bartlett/sarif-php-sdk/src/Property/Description.phpK  rbK  J)      >   vendor/bartlett/sarif-php-sdk/src/Property/DeprecatedGuids.php  rb  ^      =   vendor/bartlett/sarif-php-sdk/src/Property/MessageStrings.phpS  rbS  КsѤ      2   vendor/bartlett/sarif-php-sdk/src/Property/Uri.php  rb  +]R      4   vendor/bartlett/sarif-php-sdk/src/Property/Kinds.php{  rb{  (Ȥ      A   vendor/bartlett/sarif-php-sdk/src/Property/FullyQualifiedName.phpQ  rbQ  q      8   vendor/bartlett/sarif-php-sdk/src/Property/UriBaseId.php  rb  JJ      >   vendor/bartlett/sarif-php-sdk/src/Property/DeprecatedNames.phpd  rbd  G      9   vendor/bartlett/sarif-php-sdk/src/Property/ByteLength.php  rb  ^I      5   vendor/bartlett/sarif-php-sdk/src/Property/Binary.php  rb  ITݤ      4   vendor/bartlett/sarif-php-sdk/src/Property/Stdin.phpE  rbE  P      ;   vendor/bartlett/sarif-php-sdk/src/Property/Translations.php  rb  n;      7   vendor/bartlett/sarif-php-sdk/src/Property/Language.php)  rb)  B<      <   vendor/bartlett/sarif-php-sdk/src/Property/RunGraphIndex.php  rb  X      ?   vendor/bartlett/sarif-php-sdk/src/Property/LocationArtifact.php  rb  %      ?   vendor/bartlett/sarif-php-sdk/src/Property/OffsetFromParent.php:  rb:  {[      ?   vendor/bartlett/sarif-php-sdk/src/Property/IsComprehensible.php=  rb=  S;      5   vendor/bartlett/sarif-php-sdk/src/Property/Offset.php  rb  	      B   vendor/bartlett/sarif-php-sdk/src/Property/LocationSuppression.phpE  rbE  S      7   vendor/bartlett/sarif-php-sdk/src/Property/FullName.php  rb  65      Z   vendor/bartlett/sarif-php-sdk/src/Property/MinimumRequiredLocalizedDataSemanticVersion.php   rb   xE̤      ;   vendor/bartlett/sarif-php-sdk/src/Property/StartTimeUtc.php  rb  /u;      5   vendor/bartlett/sarif-php-sdk/src/Property/EdgeId.php  rb  8      7   vendor/bartlett/sarif-php-sdk/src/Property/MappedTo.phpZ  rbZ  #O!      9   vendor/bartlett/sarif-php-sdk/src/Property/ByteOffset.php  rb  Z=~      9   vendor/bartlett/sarif-php-sdk/src/Property/Provenance.phph  rbh  l)      :   vendor/bartlett/sarif-php-sdk/src/Property/StartColumn.php  rb  Y֤      9   vendor/bartlett/sarif-php-sdk/src/Property/ColumnKind.php  rb  	x      K   vendor/bartlett/sarif-php-sdk/src/Property/SpecialSignificanceLocations.php  rb  fE      5   vendor/bartlett/sarif-php-sdk/src/Property/Schema.php  rb  -      8   vendor/bartlett/sarif-php-sdk/src/Property/CodeFlows.php  rb  L\ä      =   vendor/bartlett/sarif-php-sdk/src/Property/InformationUri.php5  rb5  8Y      5   vendor/bartlett/sarif-php-sdk/src/Property/Length.php  rb  
ͤ      8   vendor/bartlett/sarif-php-sdk/src/Property/ProcessId.php	  rb	  7      6   vendor/bartlett/sarif-php-sdk/src/Property/Regions.php  rb  n      B   vendor/bartlett/sarif-php-sdk/src/Property/SupportedTaxonomies.phpY  rbY  M      :   vendor/bartlett/sarif-php-sdk/src/Property/Invocations.php  rb  kV      A   vendor/bartlett/sarif-php-sdk/src/Property/ExecutableLocation.php  rb  i4      5   vendor/bartlett/sarif-php-sdk/src/Property/Stderr.phpL  rbL  p<      G   vendor/bartlett/sarif-php-sdk/src/Property/VersionControlProvenance.phpu  rbu  j      K   vendor/bartlett/sarif-php-sdk/src/Property/LocalizedDataSemanticVersion.php  rb  	ʤ      3   vendor/bartlett/sarif-php-sdk/src/Property/Taxa.php  rb  ^鏤      >   vendor/bartlett/sarif-php-sdk/src/Property/AddressLocation.php8  rb8        <   vendor/bartlett/sarif-php-sdk/src/Property/ContextRegion.phpU  rbU  Z      6   vendor/bartlett/sarif-php-sdk/src/Property/EndLine.php  rb  hҤ      <   vendor/bartlett/sarif-php-sdk/src/Property/Justification.php.  rb.  H      9   vendor/bartlett/sarif-php-sdk/src/Property/Descriptor.php  rb  !L      8   vendor/bartlett/sarif-php-sdk/src/Property/Addresses.php  rb  aڤ      :   vendor/bartlett/sarif-php-sdk/src/Property/RevisionTag.php   rb   Z޽Ť      J   vendor/bartlett/sarif-php-sdk/src/Property/TranslationMetadataRequired.php  rb  q       ?   vendor/bartlett/sarif-php-sdk/src/Property/NewlineSequences.php  rb  c      3   vendor/bartlett/sarif-php-sdk/src/Property/Name.php  rb        9   vendor/bartlett/sarif-php-sdk/src/Property/Parameters.php  rb  dض      9   vendor/bartlett/sarif-php-sdk/src/Property/StatusCode.php  rb  J      >   vendor/bartlett/sarif-php-sdk/src/Property/InnerExceptions.php   rb   t+      D   vendor/bartlett/sarif-php-sdk/src/Property/DefaultSourceLanguage.phpf  rbf  n{      >   vendor/bartlett/sarif-php-sdk/src/Property/GraphTraversals.php  rb  ^      B   vendor/bartlett/sarif-php-sdk/src/Property/InvocationConverter.phpY  rbY  o      V   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceExtensions.php;  rb;  i1c      ;   vendor/bartlett/sarif-php-sdk/src/Property/LocationKind.php  rb  	2x      =   vendor/bartlett/sarif-php-sdk/src/Property/TaxaReferences.php  rb  T      A   vendor/bartlett/sarif-php-sdk/src/Property/LocationAttachment.php  rb  ߤ      @   vendor/bartlett/sarif-php-sdk/src/Property/StatusSuppression.php  rb  ac      C   vendor/bartlett/sarif-php-sdk/src/Property/GlobalMessageStrings.php  rb  \k%      8   vendor/bartlett/sarif-php-sdk/src/Property/Artifacts.php  rb  \Ǥ      >   vendor/bartlett/sarif-php-sdk/src/Property/AbsoluteAddress.php  rb        V   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceTaxonomies.php8  rb8  e\x      6   vendor/bartlett/sarif-php-sdk/src/Property/Snippet.phpO  rbO  @      ;   vendor/bartlett/sarif-php-sdk/src/Property/Organization.php'  rb'  #QO      X   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceTranslations.phpO  rbO  ز      ;   vendor/bartlett/sarif-php-sdk/src/Property/NestingLevel.php  rb  "TM      :   vendor/bartlett/sarif-php-sdk/src/Property/ParentIndex.php  rb  WW      3   vendor/bartlett/sarif-php-sdk/src/Property/Text.php  rb  A      8   vendor/bartlett/sarif-php-sdk/src/Property/Arguments.php  rb  Τ      4   vendor/bartlett/sarif-php-sdk/src/Property/Nodes.php  rb  8κT      @   vendor/bartlett/sarif-php-sdk/src/Property/ConversionProcess.phpW  rbW  +J      4   vendor/bartlett/sarif-php-sdk/src/Property/Right.php  rb  TǤ      9   vendor/bartlett/sarif-php-sdk/src/Property/Extensions.php9  rb9        =   vendor/bartlett/sarif-php-sdk/src/Property/ExitSignalName.php5  rb5  @M      3   vendor/bartlett/sarif-php-sdk/src/Property/Runs.php
  rb
  xW      8   vendor/bartlett/sarif-php-sdk/src/Property/RuleIndex.php  rb  l      \   vendor/bartlett/sarif-php-sdk/src/Property/ExternalPropertyFileReferenceLogicalLocations.phpw  rbw        4   vendor/bartlett/sarif-php-sdk/src/Property/Fixes.php  rb        9   vendor/bartlett/sarif-php-sdk/src/Property/RevisionId.php  rb  ?Vw      5   vendor/bartlett/sarif-php-sdk/src/Property/Hashes.php  rb  BcU      :   vendor/bartlett/sarif-php-sdk/src/Property/DisplayBase.phpo  rbo  {      9   vendor/bartlett/sarif-php-sdk/src/Property/Taxonomies.php  rb  I      ?   vendor/bartlett/sarif-php-sdk/src/Internal/JsonSerializable.phpH  rbH  vӤ      <   vendor/bartlett/sarif-php-sdk/src/Definition/WebResponse.php  rb  WE      5   vendor/bartlett/sarif-php-sdk/src/Definition/Tool.php  rb  k3      <   vendor/bartlett/sarif-php-sdk/src/Definition/PropertyBag.php  rb  a      7   vendor/bartlett/sarif-php-sdk/src/Definition/Region.php  rb  ;      ;   vendor/bartlett/sarif-php-sdk/src/Definition/Conversion.php  rb  ,      A   vendor/bartlett/sarif-php-sdk/src/Definition/ArtifactLocation.php  rb  Ǚf      @   vendor/bartlett/sarif-php-sdk/src/Definition/ArtifactContent.phpn  rbn  /3      E   vendor/bartlett/sarif-php-sdk/src/Definition/RunAutomationDetails.php  rb  
      A   vendor/bartlett/sarif-php-sdk/src/Definition/PhysicalLocation.phpX  rbX  =;      D   vendor/bartlett/sarif-php-sdk/src/Definition/TranslationMetadata.php  rb  2j      5   vendor/bartlett/sarif-php-sdk/src/Definition/Edge.php  rb  O}f      <   vendor/bartlett/sarif-php-sdk/src/Definition/Replacement.php!  rb!        8   vendor/bartlett/sarif-php-sdk/src/Definition/Address.php
  rb
  k      F   vendor/bartlett/sarif-php-sdk/src/Definition/ConfigurationOverride.php  rb  {/      >   vendor/bartlett/sarif-php-sdk/src/Definition/ToolComponent.php0  rb0        ;   vendor/bartlett/sarif-php-sdk/src/Definition/Invocation.php  rb  j      C   vendor/bartlett/sarif-php-sdk/src/Definition/ThreadFlowLocation.php  rb  S      7   vendor/bartlett/sarif-php-sdk/src/Definition/Result.php9  rb9  u      <   vendor/bartlett/sarif-php-sdk/src/Definition/Suppression.php  rb  `4      ?   vendor/bartlett/sarif-php-sdk/src/Definition/GraphTraversal.php
  rb
  s      :   vendor/bartlett/sarif-php-sdk/src/Definition/Rectangle.php  rb  B      >   vendor/bartlett/sarif-php-sdk/src/Definition/EdgeTraversal.phpa  rba  Yp      4   vendor/bartlett/sarif-php-sdk/src/Definition/Run.php  rb  8      M   vendor/bartlett/sarif-php-sdk/src/Definition/ReportingDescriptorReference.phpD  rbD  ]      P   vendor/bartlett/sarif-php-sdk/src/Definition/ReportingDescriptorRelationship.php  rb  
0      4   vendor/bartlett/sarif-php-sdk/src/Definition/Fix.php  rb  IP      6   vendor/bartlett/sarif-php-sdk/src/Definition/Graph.phpN  rbN  )9h      5   vendor/bartlett/sarif-php-sdk/src/Definition/Node.php  rb  Yk      9   vendor/bartlett/sarif-php-sdk/src/Definition/CodeFlow.phpx  rbx  2u      ;   vendor/bartlett/sarif-php-sdk/src/Definition/ThreadFlow.php  rb  0?      9   vendor/bartlett/sarif-php-sdk/src/Definition/Artifact.php
  rb
  ɿ      ;   vendor/bartlett/sarif-php-sdk/src/Definition/WebRequest.php  rb  1yz      6   vendor/bartlett/sarif-php-sdk/src/Definition/Stack.php  rb  B/      9   vendor/bartlett/sarif-php-sdk/src/Definition/Location.php  rb  hj      G   vendor/bartlett/sarif-php-sdk/src/Definition/ReportingConfiguration.phpR  rbR  ~I|      ?   vendor/bartlett/sarif-php-sdk/src/Definition/ArtifactChange.php  rb        F   vendor/bartlett/sarif-php-sdk/src/Definition/VersionControlDetails.phpf  rbf  #^*      A   vendor/bartlett/sarif-php-sdk/src/Definition/ResultProvenance.php 
  rb 
        E   vendor/bartlett/sarif-php-sdk/src/Definition/LocationRelationship.php  rb         A   vendor/bartlett/sarif-php-sdk/src/Definition/SpecialLocations.php5  rb5  /      G   vendor/bartlett/sarif-php-sdk/src/Definition/ToolComponentReference.php?  rb?  B"      D   vendor/bartlett/sarif-php-sdk/src/Definition/ReportingDescriptor.php  rb  H=pm      ;   vendor/bartlett/sarif-php-sdk/src/Definition/Attachment.php  rb  +8ڤ      =   vendor/bartlett/sarif-php-sdk/src/Definition/Notification.phpz	  rbz	  ؤ      :   vendor/bartlett/sarif-php-sdk/src/Definition/Exception.php  rb  84      N   vendor/bartlett/sarif-php-sdk/src/Definition/ExternalPropertyFileReference.php  rb  l7      I   vendor/bartlett/sarif-php-sdk/src/Definition/MultiformatMessageString.phpq  rbq  <      8   vendor/bartlett/sarif-php-sdk/src/Definition/Message.php  rb  w      ;   vendor/bartlett/sarif-php-sdk/src/Definition/StackFrame.php  rb  "      O   vendor/bartlett/sarif-php-sdk/src/Definition/ExternalPropertyFileReferences.php'  rb'  09      @   vendor/bartlett/sarif-php-sdk/src/Definition/LogicalLocation.php
  rb
  LΤ      C   vendor/bartlett/sarif-php-sdk/src/Definition/ExternalProperties.php  rb  LW      .   vendor/bartlett/sarif-php-sdk/src/SarifLog.php  rb  EC1         vendor/brightzone/.DS_Store   rb   gs      +   vendor/brightzone/gremlin-php/composer.lockٓ rbٓ eH6      '   vendor/brightzone/gremlin-php/.DS_Store  rb  P      *   vendor/brightzone/gremlin-php/CHANGELOG.md  rb  ,      (   vendor/brightzone/gremlin-php/.simplecov   rb   "{      '   vendor/brightzone/gremlin-php/build.xml&  rb&  B      0   vendor/brightzone/gremlin-php/tests/AuthTest.php  rb  9U{ʤ      3   vendor/brightzone/gremlin-php/tests/AuthGS3Test.php  rb  e      8   vendor/brightzone/gremlin-php/tests/Stubs/Connection.php  rb  3Ĥ      I   vendor/brightzone/gremlin-php/tests/Stubs/IncorrectlyFormattedMessage.php  rb  F      L   vendor/brightzone/gremlin-php/tests/Stubs/IncorrectlyFormattedConnection.php  rb  >y      <   vendor/brightzone/gremlin-php/tests/Stubs/TestSerializer.phpb  rbb        5   vendor/brightzone/gremlin-php/tests/GraphSon3Test.php  rb  C'      9   vendor/brightzone/gremlin-php/tests/GremlinServerTest.phpV%  rbV%  ou      3   vendor/brightzone/gremlin-php/tests/RexsterTest.php_  rb_  +ʤ      >   vendor/brightzone/gremlin-php/tests/RexsterExamplesGS3Test.php  rb  ן|      <   vendor/brightzone/gremlin-php/tests/GremlinServerGS3Test.phpU  rbU   >      :   vendor/brightzone/gremlin-php/tests/RexsterWithGS3Test.php  rb  ^Ft      >   vendor/brightzone/gremlin-php/tests/RexsterTransactionTest.php;  rb;  |m      /   vendor/brightzone/gremlin-php/tests/webtest.phpF  rbF  ~Ea      6   vendor/brightzone/gremlin-php/tests/RexsterGS3Test.php  rb  ?@{      1   vendor/brightzone/gremlin-php/tests/bootstrap.php:   rb:   X<      7   vendor/brightzone/gremlin-php/tests/RexsterTestCase.php  rb  J!      ;   vendor/brightzone/gremlin-php/tests/RexsterTestExamples.phpT  rbT  da      ;   vendor/brightzone/gremlin-php/tests/RexsterExamplesTest.phpl  rbl  ay6      :   vendor/brightzone/gremlin-php/tests/RexsterTestWithGS3.php.  rb.  '%      A   vendor/brightzone/gremlin-php/tests/RexsterTransactionGS3Test.phpH  rbH  Jtߤ      '   vendor/brightzone/gremlin-php/README.md0  rb0  rl      (   vendor/brightzone/gremlin-php/.gitignore`   rb`   6      .   vendor/brightzone/gremlin-php/.scrutinizer.yml  rb  Hp      /   vendor/brightzone/gremlin-php/build/phpunit.xmlm  rbm  R[O      5   vendor/brightzone/gremlin-php/build/server/install.shM  rbM  :Τ      H   vendor/brightzone/gremlin-php/build/server/3.2.x/gremlin-server-php.yaml
  rb
  OI      Q   vendor/brightzone/gremlin-php/build/server/3.2.x/gremlin-php-script-secure.groovy	  rb	  ./c      G   vendor/brightzone/gremlin-php/build/server/3.2.x/neo4j-empty.properties  rb        O   vendor/brightzone/gremlin-php/build/server/3.2.x/gremlin-server-php-secure.yaml   rb   P      J   vendor/brightzone/gremlin-php/build/server/3.2.x/gremlin-php-script.groovyQ  rbQ  IP      :   vendor/brightzone/gremlin-php/build/server/jdk8-install.sh  rb  !      H   vendor/brightzone/gremlin-php/build/server/3.3.x/gremlin-server-php.yaml  rb  m7      Q   vendor/brightzone/gremlin-php/build/server/3.3.x/gremlin-php-script-secure.groovy	  rb	  ./c      X   vendor/brightzone/gremlin-php/build/server/3.3.x/gremlin-server-php-secure-graphson.yaml  rb  BO      Q   vendor/brightzone/gremlin-php/build/server/3.3.x/gremlin-server-php-graphson.yaml  rb  ۡVѤ      G   vendor/brightzone/gremlin-php/build/server/3.3.x/neo4j-empty.propertiesV  rbV  /      O   vendor/brightzone/gremlin-php/build/server/3.3.x/gremlin-server-php-secure.yaml  rb  ^      J   vendor/brightzone/gremlin-php/build/server/3.3.x/gremlin-php-script.groovyQ  rbQ  IP      M   vendor/brightzone/gremlin-php/build/server/janusGraph/gremlin-server-php.yamlg	  rbg	        V   vendor/brightzone/gremlin-php/build/server/janusGraph/gremlin-php-script-secure.groovy	  rb	  ./c      L   vendor/brightzone/gremlin-php/build/server/janusGraph/neo4j-empty.properties  rb        T   vendor/brightzone/gremlin-php/build/server/janusGraph/gremlin-server-php-secure.yaml  rb  36      O   vendor/brightzone/gremlin-php/build/server/janusGraph/gremlin-php-script.groovyQ  rbQ  IP      H   vendor/brightzone/gremlin-php/build/server/3.4.x/gremlin-server-php.yaml  rb  m7      Q   vendor/brightzone/gremlin-php/build/server/3.4.x/gremlin-php-script-secure.groovy	  rb	  ./c      X   vendor/brightzone/gremlin-php/build/server/3.4.x/gremlin-server-php-secure-graphson.yamlZ  rbZ  $k      Q   vendor/brightzone/gremlin-php/build/server/3.4.x/gremlin-server-php-graphson.yaml  rb  ۡVѤ      G   vendor/brightzone/gremlin-php/build/server/3.4.x/neo4j-empty.propertiesV  rbV  /      O   vendor/brightzone/gremlin-php/build/server/3.4.x/gremlin-server-php-secure.yamlZ  rbZ  
      J   vendor/brightzone/gremlin-php/build/server/3.4.x/gremlin-php-script.groovyQ  rbQ  IP      .   vendor/brightzone/gremlin-php/build/phpdox.xml  rb  d4      -   vendor/brightzone/gremlin-php/build/phpmd.xml  rb  ;Rl      -   vendor/brightzone/gremlin-php/build/phpcs.xml  rb  c*[      -   vendor/brightzone/gremlin-php/build/deploy.sh=  rb=  0Ԇ      )   vendor/brightzone/gremlin-php/LICENSE.txt0  rb0  1Ts       )   vendor/brightzone/gremlin-php/.travis.ymlM  rbM  &      +   vendor/brightzone/gremlin-php/composer.json  rb  '=      0   vendor/brightzone/gremlin-php/src/Connection.phpm  rbm  4ڤ      .   vendor/brightzone/gremlin-php/src/Workload.php  rb  Zb      5   vendor/brightzone/gremlin-php/src/ServerException.php  rb  	c      4   vendor/brightzone/gremlin-php/src/RequestMessage.php  rb  ɬ      +   vendor/brightzone/gremlin-php/src/.DS_Store  rb  r      E   vendor/brightzone/gremlin-php/src/Serializers/SerializerInterface.php4  rb4  :E      6   vendor/brightzone/gremlin-php/src/Serializers/Json.phpJ  rbJ  ؤ      7   vendor/brightzone/gremlin-php/src/Serializers/Gson3.phpM=  rbM=  
      ,   vendor/brightzone/gremlin-php/src/Helper.php^  rb^  w      -   vendor/brightzone/gremlin-php/src/Message.php   rb   A      7   vendor/brightzone/gremlin-php/src/InternalException.php|  rb|  A(      -   vendor/symfony/polyfill-ctype/bootstrap80.phpr  rbr  F)      %   vendor/symfony/polyfill-ctype/LICENSE)  rb)  `e0      +   vendor/symfony/polyfill-ctype/bootstrap.php@  rb@  jQ9      '   vendor/symfony/polyfill-ctype/README.md`  rb`  j      '   vendor/symfony/polyfill-ctype/Ctype.php  rb  xs      +   vendor/symfony/polyfill-ctype/composer.json	  rb	  N         vendor/symfony/yaml/LICENSE)  rb)  ax         vendor/symfony/yaml/Parser.phpK  rbK  1<          vendor/symfony/yaml/CHANGELOG.md6  rb6  W         vendor/symfony/yaml/Escaper.php  rb  ]      +   vendor/symfony/yaml/Resources/bin/yaml-linty  rby  h5         vendor/symfony/yaml/README.md  rb  ـT      !   vendor/symfony/yaml/Unescaper.php  rb  ,         vendor/symfony/yaml/Dumper.phpy  rby  <         vendor/symfony/yaml/Inline.php_  rb_  /2Ȥ         vendor/symfony/yaml/Yaml.phpq  rbq  
t      +   vendor/symfony/yaml/Command/LintCommand.php'  rb'  &      /   vendor/symfony/yaml/Exception/DumpException.php  rb        4   vendor/symfony/yaml/Exception/ExceptionInterface.php  rb  B9      2   vendor/symfony/yaml/Exception/RuntimeException.php  rb  _q      0   vendor/symfony/yaml/Exception/ParseException.phpj  rbj  BU      '   vendor/symfony/yaml/Tag/TaggedValue.php  rb  n%      !   vendor/symfony/yaml/composer.json  rb  ߨ      ,   vendor/symfony/deprecation-contracts/LICENSE)  rb)  2      1   vendor/symfony/deprecation-contracts/CHANGELOG.md   rb   h{#      1   vendor/symfony/deprecation-contracts/function.php  rb  Oݤ      .   vendor/symfony/deprecation-contracts/README.md  rb  3      /   vendor/symfony/deprecation-contracts/.gitignore"   rb"   U      2   vendor/symfony/deprecation-contracts/composer.jsonK  rbK           media/devfaceted/index.html  rb  󲒤         media/devfaceted/.DS_Store   rb   W      &   media/devfaceted/devfaceted/index.html  rb  󲒤      %   media/devfaceted/devfaceted/.DS_Store   rb   N1դ      4   media/devfaceted/devfaceted/data/sortable_table.html  rb  ox+      ;   media/devfaceted/devfaceted/data/class_constant_counts.html	  rb	  w      -   media/devfaceted/devfaceted/data/appinfo.html3  rb3  Ӥ      ,   media/devfaceted/devfaceted/data/level1.htmlU  rbU  ?e      7   media/devfaceted/devfaceted/data/foreach_favorites.html  rb  ¤      6   media/devfaceted/devfaceted/data/parameters_names.html  rb  DoI      3   media/devfaceted/devfaceted/data/method_counts.html	  rb	  w      3   media/devfaceted/devfaceted/data/classes_depth.html  rb  *k      ,   media/devfaceted/devfaceted/data/levels.html  rb  2jL      7   media/devfaceted/devfaceted/data/external_services.html[  rb[  wؤ      ,   media/devfaceted/devfaceted/data/weekly.html  rb  zX      9   media/devfaceted/devfaceted/data/favorites_dashboard.htmlL  rbL  )z      ;   media/devfaceted/devfaceted/data/local_variable_counts.html  rb  C$      5   media/devfaceted/devfaceted/data/deadcode_issues.html  rb        +   media/devfaceted/devfaceted/data/stats.html  rb  OF1      1   media/devfaceted/devfaceted/data/index_melis.htmlC
  rbC
  UMޤ      5   media/devfaceted/devfaceted/data/menuMigration73.html  rb  1C      4   media/devfaceted/devfaceted/data/annex_settings.html  rb  DƏ(      +   media/devfaceted/devfaceted/data/index.html  rb  Ƞ       *   media/devfaceted/devfaceted/data/.DS_Store  rb  K譗      ,   media/devfaceted/devfaceted/data/review.html  rb  6턤      0   media/devfaceted/devfaceted/data/proc_files.html`  rb`  YҤ      5   media/devfaceted/devfaceted/data/php_compilation.htmlB  rbB        *   media/devfaceted/devfaceted/data/base.html   rb   ^eǤ      2   media/devfaceted/devfaceted/data/annex_config.html  rb  JN      4   media/devfaceted/devfaceted/data/directive_list.html2  rb2  U3      4   media/devfaceted/devfaceted/data/extension_list.html  rb  M      6   media/devfaceted/devfaceted/data/fixes_phpcsfixer.html  rb  *      0   media/devfaceted/devfaceted/data/images/logo.png!T  rb!T  Ĥ      5   media/devfaceted/devfaceted/data/index_migration.html/	  rb/	        2   media/devfaceted/devfaceted/data/dynamic_code.html  rb  =!Ƥ      8   media/devfaceted/devfaceted/data/fossilized_methods.html  rb  m      /   media/devfaceted/devfaceted/data/neoissues.htmlM  rbM  uq      1   media/devfaceted/devfaceted/data/inventories.html  rb  Ф      2   media/devfaceted/devfaceted/data/analyses_doc.html  rb  ä      :   media/devfaceted/devfaceted/data/compatibility_issues.html  rb  i      ,   media/devfaceted/devfaceted/data/issues.html  rb  6턤      4   media/devfaceted/devfaceted/data/typehint_stats.htmll  rbl  -Լˤ      *   media/devfaceted/devfaceted/data/menu.html  rb   U      +   media/devfaceted/devfaceted/data/files.htmlj  rbj  
      5   media/devfaceted/devfaceted/data/identical_files.html  rb  bɤ      )   media/devfaceted/devfaceted/data/pmb.html  rb  6턤      8   media/devfaceted/devfaceted/data/indentation_levels.html  rb  NDŤ      +   media/devfaceted/devfaceted/data/codes.html  rb  7      :   media/devfaceted/devfaceted/data/dereferencing_levels.html  rb  ƒm      6   media/devfaceted/devfaceted/data/favorites_issues.html  rb  J<v      .   media/devfaceted/devfaceted/data/bugfixes.htmlS  rbS  *,      2   media/devfaceted/devfaceted/data/styles/vendor.cssσ rbσ uh      0   media/devfaceted/devfaceted/data/styles/main.cssO rbO 39ݤ      2   media/devfaceted/devfaceted/data/styles/exakat.css~   rb~   F;      0   media/devfaceted/devfaceted/data/styles/tree.css  rb  m:      1   media/devfaceted/devfaceted/data/suggestions.html8  rb8        9   media/devfaceted/devfaceted/data/complex_expressions.htmlL  rbL  p%ؤ      5   media/devfaceted/devfaceted/data/security_issues.html  rb  %B      3   media/devfaceted/devfaceted/data/used_settings.htmlT  rbT  #G      3   media/devfaceted/devfaceted/data/compatibility.html  rb  CW      /   media/devfaceted/devfaceted/data/no_issues.html  rb  !      5   media/devfaceted/devfaceted/data/property_counts.html  rb  3"+      ,   media/devfaceted/devfaceted/data/level4.htmlN  rbN  4      :   media/devfaceted/devfaceted/data/scripts/highlight.pack.js:1  rb:1  Dä      9   media/devfaceted/devfaceted/data/scripts/clipboard.min.js*  rb*  q7      2   media/devfaceted/devfaceted/data/scripts/exakat.js!   rb!   ={z      2   media/devfaceted/devfaceted/data/scripts/vendor.js
 rb
 7N      6   media/devfaceted/devfaceted/data/scripts/datatables.js  rb  gi      9   media/devfaceted/devfaceted/data/scripts/facetedsearch.js4  rb4  T?ݤ      5   media/devfaceted/devfaceted/data/scripts/dashboard.jsj  rbj  Bl:      9   media/devfaceted/devfaceted/data/performances_issues.html  rb  .p      9   media/devfaceted/devfaceted/data/variables_confusing.html
  rb
  ؤ      8   media/devfaceted/devfaceted/data/altered_directives.html  rb  ͤ      <   media/devfaceted/devfaceted/data/compatibility_shopware.htmly  rby  b      -   media/devfaceted/devfaceted/data/ext_lib.html  rb  OQ      6   media/devfaceted/devfaceted/data/parameter_counts.html  rb  ]}      5   media/devfaceted/devfaceted/data/changed_classes.html  rb  
mޤ      ,   media/devfaceted/devfaceted/data/level3.htmlN  rbN  $ؤ      .   media/devfaceted/devfaceted/data/analyses.html3  rb3  g      3   media/devfaceted/devfaceted/data/annex_ruleset.html  rb  ϱ      2   media/devfaceted/devfaceted/data/index_weekly.html  rb  jˤ      .   media/devfaceted/devfaceted/data/cit_size.html  rb  ~!      2   media/devfaceted/devfaceted/data/menuShopware.html  rb  Tˤ      +   media/devfaceted/devfaceted/data/robots.txt+   rb+   i      -   media/devfaceted/devfaceted/data/credits.html  rb  >      3   media/devfaceted/devfaceted/data/proc_analyses.html  rb  9a      9   media/devfaceted/devfaceted/data/concentrated_issues.html  rb  )wŤ      >   media/devfaceted/devfaceted/data/fonts/fontawesome-webfont.svg rb Jt      6   media/devfaceted/devfaceted/data/fonts/FontAwesome.otf< rb< CǤ      H   media/devfaceted/devfaceted/data/fonts/glyphicons-halflings-regular.woff[  rb[  {      G   media/devfaceted/devfaceted/data/fonts/glyphicons-halflings-regular.eotN  rbN  XǱ      0   media/devfaceted/devfaceted/data/fonts/.DS_Store  rb  j m      G   media/devfaceted/devfaceted/data/fonts/sourcesanspro-bold-webfont.woff2[  rb[  GǤ      J   media/devfaceted/devfaceted/data/fonts/sourcesanspro-regular-webfont.woff2\  rb\  S      I   media/devfaceted/devfaceted/data/fonts/glyphicons-halflings-regular.woff2lF  rblF  va      G   media/devfaceted/devfaceted/data/fonts/glyphicons-halflings-regular.ttf\  rb\  <      @   media/devfaceted/devfaceted/data/fonts/fontawesome-webfont.woff2 rb ȗȤ      >   media/devfaceted/devfaceted/data/fonts/fontawesome-webfont.ttfT rbT _      ?   media/devfaceted/devfaceted/data/fonts/fontawesome-webfont.woff,a rb,a ,kq#      G   media/devfaceted/devfaceted/data/fonts/glyphicons-halflings-regular.svg¨ rb¨ |ɤ      F   media/devfaceted/devfaceted/data/fonts/sourcesanspro-bold-webfont.woffr  rbr  dzp      >   media/devfaceted/devfaceted/data/fonts/fontawesome-webfont.eot* rb* ^      I   media/devfaceted/devfaceted/data/fonts/sourcesanspro-regular-webfont.woffdt  rbdt  2      @   media/devfaceted/devfaceted/data/compatibility_compilations.html  rb  5!      +   media/devfaceted/devfaceted/data/empty.html  rb  w      4   media/devfaceted/devfaceted/data/error_messages.html  rb  A      -   media/devfaceted/devfaceted/data/globals.htmll  rbl  !      2   media/devfaceted/devfaceted/data/fixes_rector.html  rb  ;^      ,   media/devfaceted/devfaceted/data/level2.html  rb  ##+      )   media/devfaceted/data/sortable_table.html  rb  ox+      0   media/devfaceted/data/class_constant_counts.html	  rb	  w      "   media/devfaceted/data/appinfo.html3  rb3  Ӥ      !   media/devfaceted/data/level1.htmlU  rbU  ?e      ,   media/devfaceted/data/foreach_favorites.html  rb  ¤      +   media/devfaceted/data/parameters_names.html  rb  DoI      (   media/devfaceted/data/method_counts.html	  rb	  w      %   media/devfaceted/data/attributes.html  rb  1x      (   media/devfaceted/data/classes_depth.html  rb  *k      !   media/devfaceted/data/levels.html  rb  2jL      ,   media/devfaceted/data/external_services.html[  rb[  wؤ      !   media/devfaceted/data/weekly.html  rb  zX      .   media/devfaceted/data/favorites_dashboard.htmlL  rbL  )z      0   media/devfaceted/data/extended_dependencies.html  rb  ɤ      0   media/devfaceted/data/local_variable_counts.html  rb  C$      *   media/devfaceted/data/deadcode_issues.html  rb            media/devfaceted/data/stats.html  rb  OF1      &   media/devfaceted/data/index_melis.htmlC
  rbC
  UMޤ      *   media/devfaceted/data/menuMigration73.html  rb  1C      )   media/devfaceted/data/annex_settings.html  rb  DƏ(          media/devfaceted/data/index.html  rb  Ƞ          media/devfaceted/data/.DS_Store  rb  K譗      (   media/devfaceted/data/classes_sizes.htmly  rby  Җä      !   media/devfaceted/data/review.html  rb  6턤      %   media/devfaceted/data/proc_files.html`  rb`  YҤ      *   media/devfaceted/data/php_compilation.htmlB  rbB           media/devfaceted/data/base.html   rb   ^eǤ      '   media/devfaceted/data/annex_config.html  rb  JN      )   media/devfaceted/data/directive_list.html2  rb2  U3      )   media/devfaceted/data/extension_list.html  rb  M      +   media/devfaceted/data/fixes_phpcsfixer.html  rb  *      %   media/devfaceted/data/images/logo.png!T  rb!T  Ĥ      *   media/devfaceted/data/index_migration.html/	  rb/	        '   media/devfaceted/data/dynamic_code.html  rb  =!Ƥ      -   media/devfaceted/data/fossilized_methods.html  rb  m      $   media/devfaceted/data/neoissues.htmlM  rbM  uq      &   media/devfaceted/data/inventories.html  rb  Ф      '   media/devfaceted/data/analyses_doc.html  rb  ä      /   media/devfaceted/data/compatibility_issues.html  rb  i      !   media/devfaceted/data/issues.html  rb  6턤      )   media/devfaceted/data/typehint_stats.htmll  rbl  -Լˤ         media/devfaceted/data/menu.html  rb   U          media/devfaceted/data/files.htmlj  rbj  
      *   media/devfaceted/data/identical_files.html  rb  bɤ         media/devfaceted/data/pmb.html  rb  6턤      -   media/devfaceted/data/indentation_levels.html  rb  NDŤ          media/devfaceted/data/codes.html  rb  7      /   media/devfaceted/data/dereferencing_levels.html  rb  ƒm      +   media/devfaceted/data/favorites_issues.html  rb  J<v      #   media/devfaceted/data/bugfixes.htmlS  rbS  *,      '   media/devfaceted/data/styles/vendor.cssσ rbσ uh      %   media/devfaceted/data/styles/main.cssO rbO 39ݤ      '   media/devfaceted/data/styles/exakat.css~   rb~   F;      %   media/devfaceted/data/styles/tree.css  rb  m:      &   media/devfaceted/data/suggestions.html8  rb8        .   media/devfaceted/data/complex_expressions.htmlL  rbL  p%ؤ      *   media/devfaceted/data/security_issues.html  rb  %B      (   media/devfaceted/data/used_settings.htmlT  rbT  #G      (   media/devfaceted/data/compatibility.html  rb  CW      $   media/devfaceted/data/no_issues.html  rb  !      *   media/devfaceted/data/property_counts.html  rb  3"+      !   media/devfaceted/data/level4.htmlN  rbN  4      /   media/devfaceted/data/scripts/highlight.pack.js:1  rb:1  Dä      .   media/devfaceted/data/scripts/clipboard.min.js*  rb*  q7      '   media/devfaceted/data/scripts/exakat.js!   rb!   ={z      '   media/devfaceted/data/scripts/vendor.js
 rb
 7N      +   media/devfaceted/data/scripts/datatables.js  rb  gi      .   media/devfaceted/data/scripts/facetedsearch.js4  rb4  T?ݤ      *   media/devfaceted/data/scripts/dashboard.jsj  rbj  Bl:      .   media/devfaceted/data/performances_issues.html  rb  .p      .   media/devfaceted/data/variables_confusing.html  rb  P      -   media/devfaceted/data/altered_directives.html  rb  ͤ      1   media/devfaceted/data/compatibility_shopware.htmly  rby  b      "   media/devfaceted/data/ext_lib.html  rb  OQ      +   media/devfaceted/data/parameter_counts.html  rb  ]}      *   media/devfaceted/data/changed_classes.html  rb  
mޤ      !   media/devfaceted/data/level3.htmlN  rbN  $ؤ      #   media/devfaceted/data/analyses.html3  rb3  g      (   media/devfaceted/data/annex_ruleset.html  rb  ϱ      '   media/devfaceted/data/index_weekly.html  rb  jˤ      #   media/devfaceted/data/cit_size.html  rb  ~!      '   media/devfaceted/data/menuShopware.html  rb  Tˤ          media/devfaceted/data/robots.txt+   rb+   i      "   media/devfaceted/data/credits.html  rb  >      (   media/devfaceted/data/proc_analyses.html  rb  9a      .   media/devfaceted/data/concentrated_issues.html  rb  )wŤ      3   media/devfaceted/data/fonts/fontawesome-webfont.svg rb Jt      +   media/devfaceted/data/fonts/FontAwesome.otf< rb< CǤ      =   media/devfaceted/data/fonts/glyphicons-halflings-regular.woff[  rb[  {      <   media/devfaceted/data/fonts/glyphicons-halflings-regular.eotN  rbN  XǱ      %   media/devfaceted/data/fonts/.DS_Store  rb  j m      <   media/devfaceted/data/fonts/sourcesanspro-bold-webfont.woff2[  rb[  GǤ      ?   media/devfaceted/data/fonts/sourcesanspro-regular-webfont.woff2\  rb\  S      >   media/devfaceted/data/fonts/glyphicons-halflings-regular.woff2lF  rblF  va      <   media/devfaceted/data/fonts/glyphicons-halflings-regular.ttf\  rb\  <      5   media/devfaceted/data/fonts/fontawesome-webfont.woff2 rb ȗȤ      3   media/devfaceted/data/fonts/fontawesome-webfont.ttfT rbT _      4   media/devfaceted/data/fonts/fontawesome-webfont.woff,a rb,a ,kq#      <   media/devfaceted/data/fonts/glyphicons-halflings-regular.svg¨ rb¨ |ɤ      ;   media/devfaceted/data/fonts/sourcesanspro-bold-webfont.woffr  rbr  dzp      3   media/devfaceted/data/fonts/fontawesome-webfont.eot* rb* ^      >   media/devfaceted/data/fonts/sourcesanspro-regular-webfont.woffdt  rbdt  2      5   media/devfaceted/data/compatibility_compilations.html  rb  5!          media/devfaceted/data/empty.html  rb  w      )   media/devfaceted/data/error_messages.html  rb  A      "   media/devfaceted/data/globals.htmll  rbl  !      '   media/devfaceted/data/fixes_rector.html  rb  ;^      !   media/devfaceted/data/level2.html  rb  ##+         exakata  rba  .Ĥ         library/helpers.phpN  rbN            library/Exakat/Log.php
  rb
  jh         library/Exakat/Phpexec.phpTG  rbTG  _         library/Exakat/Project.phpM  rbM  E         library/Exakat/Stats.php  rb  4T         library/Exakat/Container.php  rb  m?         library/Exakat/Datastore.php>  rb>  wU%         library/Exakat/Exakat.php  rb  *^         library/Exakat/Config.phpF  rbF  Ӳ          library/Exakat/GraphElements.php<b  rb<b  f      1   library/Exakat/Analyzer/Typehints/CouldBeType.phpS  rbS        8   library/Exakat/Analyzer/Structures/UnknownPregOption.phpQ  rbQ  <jj      $   library/Exakat/Analyzer/Analyzer.php;  rb;  F,      )   library/Exakat/Analyzer/MissingResult.php  rb  A      $   library/Exakat/Analyzer/Rulesets.phpl  rbl  _2      '   library/Exakat/Analyzer/RulesetsDev.php  rb  sBE      )   library/Exakat/Analyzer/RulesetsExtra.php  rb  <F      *   library/Exakat/Analyzer/RulesetsIgnore.php  rb  Ek      -   library/Exakat/Analyzer/RulesetsInterface.php_  rb_  't#      (   library/Exakat/Analyzer/RulesetsMain.php   rb   2O      $   library/Exakat/Reports/Diplomat.yamlX  rbX  .      $   library/Exakat/Reports/Emissary.yaml6  rb6  e"      <?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
declare(strict_types = 1);

namespace Exakat\Analyzer\Complete;


class PropagateConstants extends Complete {
    public function analyze(): void {
        $this->readConstantValue();

        $this->pushConstantValues();
        $count = $this->propagate();

        $this->setCount($count);
    }

    private function propagate(int $level = 0): int {
        $total = 0;

        //Currently handles + - * / % . << >> ** ()
        //Currently handles intval, boolean, noDelimiter (String)
        //Needs realval, arrayval

        $total += $this->processAddition();
        $total += $this->processConcatenation();
        $total += $this->processSign();
        $total += $this->processPower();
        $total += $this->processComparison();
        $total += $this->processLogical();
        $total += $this->processParenthesis();
        $total += $this->processNot();
        $total += $this->processCoalesce();
        $total += $this->processTernary();
        $total += $this->processBitshift();
        $total += $this->processMultiplication();
        $this->readConstantValue();
        $this->pushConstantValues();

        if ($total > 0 && $level < 15) {
            $total += $this->propagate($level + 1);
        }

        return $total;
    }

    private function readConstantValue() {
        display('propagating Constant value in Const');
        // fix path for constants with Const
        // noDelimiter is set at the same moment as boolean and intval. Any of them is the same
        $this->atomIs(array('Constant', 'Defineconstant'))
         ->outIs('VALUE')
         ->atomIs(array('String', 'Heredoc', 'Integer', 'Null', 'Boolean', 'Float'))
         ->setProperty('propagated', true)
         ->count();
        $res = $this->rawQuery();

        $this->atomIs(array('Constant', 'Defineconstant'))
             ->outIs('VALUE')
             ->is('propagated', true)
             ->savePropertyAs('x')
             ->back('first')

             ->outIs('NAME')
             ->hasNo('propagated')
             ->raw(<<<'GREMLIN'
 sideEffect{ 
        if ("noDelimiter" in x.keys()) {
            it.get().property("noDelimiter", x.value("noDelimiter").toString()); 
        }
        if ("intval" in x.keys()) {
            it.get().property("intval", x.value("intval")); 
        }
        if ("boolean" in x.keys()) {
            it.get().property("boolean", x.value("boolean")); 
        }
        if ("isNull" in x.keys()) {
            it.get().property("isNull", x.value("isNull")); 
        }
        if ("count" in x.keys()) {
            it.get().property("count", x.value("count")); 
        }
        it.get().property("propagated", true); 
}
GREMLIN
)
                 ->count();
            $res = $this->rawQuery();

            display( $res->toInt() . " constants inited\n");
            return $res->toInt();
        }

    private function pushConstantValues() {
        $this->atomIs(array('Constant', 'Defineconstant'))
             ->outIs('NAME')
             ->is('propagated', true)
             ->savePropertyAs('constante')
             ->back('first')

             ->outIs('DEFINITION')
             ->hasNo('propagated')
             ->raw(<<<'GREMLIN'
sideEffect{ 
        if ("intval" in constante.keys()) {
            it.get().property("intval", constante.value("intval")); 
        }
        if ("boolean" in constante.keys()) {
            it.get().property("boolean", constante.value("boolean")); 
        }
        if ("noDelimiter" in constante.keys()) {
            it.get().property("noDelimiter", constante.value("noDelimiter").toString()); 
        }
        if ("isNull" in constante.keys()) {
            it.get().property("isNull", constante.value("isNull")); 
        }
        it.get().property("propagated", true); 
}
GREMLIN
)
             ->count();
            $res = $this->rawQuery();

            display( $res->toInt() . " constants propagated\n");
            return $res->toInt();
        }

    private function processAddition() {
        display('propagating Constant value in Addition');
        // fix path for constants with Const
        $this->atomIs('Addition')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             // Split LEFT and RIGHT to ensure left is in 0
             ->filter(
                $this->side()
                     ->outIs('LEFT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('RIGHT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )

            ->raw(<<<'GREMLIN'
 filter{x.size() == 2; }.
sideEffect{ 
    if (it.get().value("token") == 'T_PLUS') {
      i = x[0] + x[1];
    } else if (it.get().value("token") == 'T_MINUS') {
      i = x[0] - x[1];
    }

    it.get().property("intval", i); 
    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("propagated", true); 
    
    i = null;
}

GREMLIN
)
             ->count();

        $res = $this->rawQuery();
        display('propagating ' . $res->toInt() . ' Addition with constants');

        return $res->toInt();
    }

    private function processConcatenation() {
        display('propagating Constant value in Concatenations');
        $this->atomIs('Concatenation')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs('CONCAT')
                     ->hasNo('noDelimiter')
             )
             ->not(
                $this->side()
                     ->outIs('CONCAT')
                     ->atomIs(array('Identifier', 'Nsname'))
                     ->hasNo('propagated')
             )
             ->raw('where( __.out("CONCAT").order().by("rank").sideEffect{ x.add( it.get().value("noDelimiter") ) }.count() )')
             ->raw(<<<'GREMLIN'
sideEffect{ 
    s = x.join("");
    it.get().property("noDelimiter", s);

    // Warning : PHP doesn't handle error that same way
    if (s.isInteger()) {
        it.get().property("intval", s.toInteger());
        it.get().property("boolean", true);
    } else {
        it.get().property("intval", 0);
        it.get().property("boolean", false);
    }
    it.get().property("propagated", true); 
    
    x = null;
}

GREMLIN
)
        ->count();

        $res = $this->rawQuery();
        display('propagating ' . $res->toInt() . ' Concatenation with constants');

        return $res->toInt();
    }

    private function processSign() {
        display('propagating Constant value in Sign');
        $this->atomIs('Sign')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs('SIGN')
                     ->hasNo('intval')
             )
             ->raw('where( __.out("SIGN").sideEffect{ x = it.get().value("intval") }.count() )')
             ->raw(<<<'GREMLIN'
sideEffect{ 
        if (it.get().value("token") == 'T_PLUS') {
            it.get().property("intval", x); 
            it.get().property("boolean", x != 0);
            it.get().property("noDelimiter", x.toString()); 
        } else if (it.get().value("token") == 'T_MINUS') {
            it.get().property("intval", -1 * x); 
            it.get().property("boolean", x != 0);
            it.get().property("noDelimiter", (-1 * x).toString()); 
        }
        it.get().property("propagated", true); 

        i = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Signs with constants');
        return $res->toInt();
    }

    private function processPower() {
        display('propagating Constant value in Power');
        // fix path for constants with Const
        $this->atomIs('Power')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs(array('LEFT', 'RIGHT'))
                     ->hasNo('noDelimiter')
             )
             // Split LEFT and RIGHT to ensure left is in 0
             ->filter(
                $this->side()
                     ->outIs('LEFT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("noDelimiter") ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('RIGHT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("noDelimiter") ) }.fold()')
             )

            ->raw(<<<'GREMLIN'
 filter{ x.size() == 2; }.
sideEffect{ 
    // Using BigInteger was failing with powwer call : the query was stuck until it dies.
    i = new BigDecimal(x[0]);
    i = i.power(x[1].toLong());

    if (i > (new BigInteger(2)).pow(63)) {
        i = 0;
    }

    it.get().property("intval", i.toLong()); 
    // Note : float are not supported, so this will be rounded
    it.get().property("boolean", i.toLong() != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("propagated", true); 

    i = null;
}

GREMLIN
)
            ->count();

            $res = $this->rawQuery();
            display('propagating ' . $res->toInt() . ' power with constants');

            return $res->toInt();
        }

        private function processComparison() {
            display('propagating Constant value in Comparison');
            // fix path for constants with Const
            $this->atomIs('Comparison')
                 ->hasNo('propagated')
                 ->initVariable('x', '[ ]')
                 ->not(
                    $this->side()
                         ->outIs(array('LEFT', 'RIGHT'))
                         ->hasNo('intval')
                 )
                 // Split LEFT and RIGHT to ensure left is in 0
                 ->filter(
                    $this->side()
                         ->outIs('LEFT')
                         ->has('intval')
                         ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
                 )
                 ->filter(
                    $this->side()
                         ->outIs('RIGHT')
                         ->has('intval')
                         ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
                 )

             ->raw(<<<'GREMLIN'
 filter{x.size() == 2; }.
sideEffect{ 
        if (it.get().value("token") == 'T_GREATER') {
          i = x[0] > x[1];
        } else if (it.get().value("token") == 'T_SMALLER') {
          i = x[0] < x[1];
        } else if (it.get().value("token") == 'T_IS_GREATER_OR_EQUAL') {
          i = x[0] >= x[1];
        } else if (it.get().value("token") == 'T_IS_SMALLER_OR_EQUAL') {
          i = x[0] <= x[1];
        } else if (it.get().value("token") == 'T_IS_EQUAL' ||
                   it.get().value("token") == 'T_IS_IDENTICAL') {
          i = x[0] == x[1];
        } else if (it.get().value("token") == 'T_IS_NOT_EQUAL'||
                   it.get().value("token") == 'T_IS_NOT_IDENTICAL') {
          i = x[0] != x[1];
        }

    it.get().property("intval", i ? 1 : 0); 
    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("propagated", true); 

    i = null;
}

GREMLIN
)
            ->count();

            $res = $this->rawQuery();
            display('propagating ' . $res->toInt() . ' comparison with constants');

            return $res->toInt();
        }

    private function processLogical() {
            display('propagating Constant value in Logical');
            // fix path for constants with Const
            $this->atomIs(self::LOGICAL_ALL)
                 ->hasNo('propagated')
                 ->initVariable('x', '[ ]')
                 ->not(
                    $this->side()
                         ->outIs(array('LEFT', 'RIGHT'))
                         ->hasNo('intval')
                 )
                 // Split LEFT and RIGHT to ensure left is in 0
                 ->filter(
                    $this->side()
                         ->outIs('LEFT')
                         ->has('intval')
                         ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
                 )
                 ->filter(
                    $this->side()
                         ->outIs('RIGHT')
                         ->has('intval')
                         ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
                 )

             ->raw(<<<'GREMLIN'
 filter{x.size() == 2; }.
sideEffect{ 
      if (it.get().value("token") == 'T_BOOLEAN_AND' ||
          it.get().value("token") == 'T_LOGICAL_AND') {
        i = (x[0] != 0) && (x[1] != 0);
      } else if (it.get().value("token") == 'T_BOOLEAN_OR' ||
                 it.get().value("token") == 'T_LOGICAL_OR') {
        i = (x[0] != 0) || (x[1] != 0);
      } else if (it.get().value("token") == 'T_LOGICAL_XOR') {
        i = (x[0] != 0) ^ (x[1] != 0);
      } else if (it.get().value("token") == 'T_AND' ||
                 it.get().value("token") == 'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG') {
        i = x[0].toLong() & x[1].toLong();
      } else if (it.get().value("token") == 'T_XOR') {
        i = x[0].toLong() ^ x[1].toLong();
      } else if (it.get().value("token") == 'T_OR') {
        i = x[0].toLong() | x[1].toLong();
      } else if (it.get().value("token") == 'T_SPACESHIP') {
          i = x[0] <=> x[1];
      } else {
        Missing_logical_case_in_constant_propagation();
      }

    it.get().property("intval", i ? 1 : 0); 
    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i ? '1' : '0'); 
    it.get().property("propagated", true); 

}
GREMLIN
)
            ->count();

            $res = $this->rawQuery();
            display('propagating ' . $res->toInt() . ' logical with constants');

            return $res->toInt();
        }

    private function processParenthesis() {
        display('propagating Constant value in Parenthesis');
        $this->atomIs('Parenthesis')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs('CODE')
                     ->hasNo('intval')
             )
             ->raw('where( __.out("CODE").sideEffect{ x = it.get() }.fold() )')
             ->raw(<<<'GREMLIN'
sideEffect{ 
    it.get().property("intval", x.value("intval")); 
    if (it.get().property("boolean") != null) {
        it.get().property("boolean", x.value("boolean"));
    }
    if ("noDelimiter" in x.keys()) {
        // Ternary, Comparison
        it.get().property("noDelimiter", x.value("noDelimiter").toString()); 
    }
    it.get().property("propagated", true); 

    x = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Signs with constants');
        return $res->toInt();
    }

    private function processNot() {
        display('propagating Constant value in Not');
        $this->atomIs('Not')
             ->hasNo('propagated')
             ->initVariable('x', 0)
             ->not(
                $this->side()
                     ->outIs('NOT')
                     ->hasNo('intval')
             )
             ->not(
                $this->side()
                     ->outIs('NOT')
                     ->hasNo('noDelimiter')
             )
             ->raw('where( __.out("NOT").sideEffect{ x = it.get() }.fold() )')
             ->raw(<<<'GREMLIN'
sideEffect{ 
    if (it.get().value("token") == 'T_BANG') {
      i = !x.value("intval");
    } else if (it.get().value("token") == 'T_TILDE') { 
      i = ~x.value("intval");
    }

    it.get().property("intval", i ? 1 : 0); 
    if (it.get().property("boolean") != null) {
        it.get().property("boolean", !x.value("boolean"));
    }
    it.get().property("noDelimiter", x.value("noDelimiter").toString()); 
    it.get().property("propagated", true); 

    x = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Not with constants');
        return $res->toInt();
    }

    private function processCoalesce() {
        display('propagating Constant value in Coalesce');
        $this->atomIs('Coalesce')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs(array('LEFT', 'RIGHT'))
                     ->hasNo('intval')
             )
             // Split LEFT and RIGHT to ensure left is in 0
             ->filter(
                $this->side()
                     ->outIs('LEFT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('RIGHT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->raw(<<<'GREMLIN'
sideEffect{ 
    if (x[0] == 0) {
      i = x[1];
    } else {
      i = x[0];
    }
    
    it.get().property("intval", i); 
    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("propagated", true); 

    i = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Coalesce with constants');
        return $res->toInt();
    }

    private function processTernary() {
        display('propagating Constant value in Ternary');
        $this->atomIs('Ternary')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs(array('CONDITION', 'THEN', 'ELSE'))
                     ->hasNo('intval')
             )
             // Split CONDITION, THEN and ELSE to ensure order
             ->filter(
                $this->side()
                     ->outIs('CONDITION')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get() ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('THEN')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get() ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('ELSE')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get() ) }.fold()')
             )
             ->raw(<<<'GREMLIN'
sideEffect{ 
    if (x[0].value("intval") == 0) {
      if (x[1].label() == 'Void') {
          i = x[0].value("intval");
      } else {
          i = x[1].value("intval");
      }
    } else {
      i = x[2].value("intval");
    }

    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("intval", i); 
    it.get().property("propagated", true); 

    i = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Ternary with constants');
        return $res->toInt();
    }

    private function processBitshift() {
        display('propagating Constant value in Bitshift');
        $this->atomIs('Bitshift')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs(array('LEFT', 'RIGHT'))
                     ->hasNo('intval')
             )
             // Split LEFT and RIGHT to ensure left is in 0
             ->filter(
                $this->side()
                     ->outIs('LEFT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('RIGHT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->raw(<<<'GREMLIN'
sideEffect{ 
    if (it.get().value("token") == 'T_SL') {
      i = x[0] << x[1];
    } else if (it.get().value("token") == 'T_SR') {
      i = x[0] >> x[1];
    }
    
    it.get().property("intval", i); 
    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("propagated", true); 

    i = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Bitshift with constants');
        return $res->toInt();
    }

    private function processMultiplication() {
        display('propagating Constant value in Multiplication');
        $this->atomIs('Multiplication')
             ->tokenIs('T_PERCENTAGE')
             ->hasNo('propagated')
             ->initVariable('x', '[ ]')
             ->not(
                $this->side()
                     ->outIs(array('LEFT', 'RIGHT'))
                     ->hasNo('intval')
             )
             // Split LEFT and RIGHT to ensure left is in 0
             ->filter(
                $this->side()
                     ->outIs('LEFT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->filter(
                $this->side()
                     ->outIs('RIGHT')
                     ->has('intval')
                     ->raw('sideEffect{ x.add( it.get().value("intval") ) }.fold()')
             )
             ->raw(<<<'GREMLIN'
sideEffect{ 
    if (it.get().value("token") == 'T_STAR') {
      i = x[0] * x[1];
    } else if (it.get().value("token") == 'T_SLASH') {
      if (x[1] != 0) {
          i = x[0] / x[1];
          i = i.setScale(0, BigDecimal.ROUND_HALF_DOWN).toInteger();
      } else {
          i = 0;
      }
    } else if (it.get().value("token") == 'T_PERCENTAGE') {
      if (x[1] != 0) {
          i = x[0] % x[1];
      } else {
          i = 0;
      }
    } // Final else is an error!
    
    it.get().property("intval", i); 
    it.get().property("boolean", i != 0);
    it.get().property("noDelimiter", i.toString()); 
    it.get().property("propagated", true); 

    i = null;
}
GREMLIN
)
           ->count();
        $res = $this->rawQuery();

        display('propagating ' . $res->toInt() . ' Multiplication with constants');
        return $res->toInt();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class NonPpp extends Analyzer {
    public function analyze(): void {
        // class x { function foo() {} }
        // trait x { static $foo; }
        $this->atomIs(self::CIT)
             ->outIs(self::CLASS_ELEMENTS)
             ->atomIs(array('Method', 'Magicmethod', 'Ppp', 'Constant'))
             ->not(
                $this->side()
                     ->outIs('PPP')
                     ->atomIs('Virtualproperty')
             )
             ->is('visibility', 'none')
             ;
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetParentDefinition extends Complete {
    public function analyze(): void {
        //parent:: -> class -> extends
        $this->atomIs('Parent', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->goToClass()
             ->outIs('EXTENDS')
             ->inIs('DEFINITION')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        //new parent -> class -> extends
        $this->atomIs('Newcall', self::WITHOUT_CONSTANTS)
             ->analyzerIsNot('self')
             ->hasNoIn('DEFINITION')
             ->fullnspathIs('\\parent', self::CASE_SENSITIVE)
             ->goToClass()
             ->outIs('EXTENDS')
             ->inIs('DEFINITION')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        //parent::$property
        $this->atomIs('Parent', self::WITHOUT_CONSTANTS)
             ->as('parent')
             ->inIs('CLASS')
             ->atomIs('Staticproperty', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->as('property')
             ->outIs('MEMBER')
             ->tokenIs('T_VARIABLE')
             ->savePropertyAs('code', 'name')
             ->back('first')
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('PPP')
             ->outIs('PPP')
             ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('property', 'origin'))
             ->addETo('DEFINITION', 'property');
        $this->prepareQuery();

        // parent::constant
        $this->atomIs('Parent', self::WITHOUT_CONSTANTS)
             ->as('parent')
             ->inIs('CLASS')
             ->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->as('constant')
             ->outIs('CONSTANT')
             ->tokenIs('T_STRING')
             ->savePropertyAs('code', 'name')
             ->back('first')
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('CONST')
             ->outIs('CONST')
             ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('constant', 'origin'))
             ->addETo('DEFINITION', 'constant');
        $this->prepareQuery();

        $this->atomIs('String', self::WITHOUT_CONSTANTS)
             ->fullnspathIs('\\\\parent', self::CASE_SENSITIVE)
             ->hasNoIn('DEFINITION')
             ->as('parent')
             ->goToClass()
             ->outIs('EXTENDS')
             ->inIs('DEFINITION')
              ->as('origin')
              ->dedup(array('parent', 'origin'))
             ->addETo('DEFINITION', 'parent');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class OverwrittenProperties extends Complete {
    public function analyze(): void {
        // class x { protected $p = 1;}
        // class xx extends x { protected $p = 1;}
        $this->atomIs(array('Propertydefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->savePropertyAs('propertyname', 'name')
              ->inIs('PPP')
              ->inIs('PPP')
              ->atomIs(self::CLASSES_TRAITS)
              ->goToAllParentsTraits(self::INCLUDE_SELF) // also covers local traits
              ->outIs('PPP')
              ->outIs('PPP')
              ->atomIs(array('Propertydefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->samePropertyAs('propertyname', 'name',  self::CASE_SENSITIVE)
              ->distinctFrom('first')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();

        // synch properties/virtual properties visibility
        $this->atomIs('Propertydefinition', self::WITHOUT_CONSTANTS)
              ->inIs('PPP')
              ->is('visibility', 'protected')
              ->back('first')
              ->inIs('OVERWRITE')
              ->atomIs('Virtualproperty')
              ->inIs('PPP')
              ->raw(<<<'GREMLIN'
 property("visibility", "protected")
.sideEffect{ it.get().property("fullcode", it.get().property("fullcode").value().toString().replace("public ", "protected "));}
GREMLIN
);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class StaticMethodsCalledFromObject extends Analyzer {
    public function analyze(): void {
        $this->atomIs(array('Method', 'Magicmethod'))
             ->hasClassTrait()
             ->is('static', true)
             ->isNot('abstract', true)
             ->outIs('NAME')
             ->values('lccode')
             ->unique();
        $staticMethods = $this->rawQuery()
                              ->toArray();

        if (empty($staticMethods)) {
            return;
        }

        $this->atomIs(array('Method', 'Magicmethod'))
             ->hasClassTrait()
             ->isNot('static', true)
             ->isNot('abstract', true)
             ->outIs('NAME')
             ->values('lccode')
             ->unique();
        $normalMethods = $this->rawQuery()
                              ->toArray();

        $methods = array_diff($staticMethods, $normalMethods);
        if (empty($methods)) {
            return;
        }

        // $a->staticMethod (Anywhere in the code)
        $this->atomIs('Methodcall')
             ->outIs('OBJECT')
             ->atomIsNot('This')
             ->back('first')
             ->outIs('METHOD')
             ->codeIs($methods, self::NO_TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $this->staticMethod (In the local class tree)
        $this->atomIs('Methodcall')
             ->outIs('OBJECT')
             ->atomIs('This')
             ->back('first')
             ->outIs('METHOD')
             ->outIs('NAME')
             ->savePropertyAs('lccode', 'name')
             ->goToClass()
             ->goToAllParents(self::INCLUDE_SELF)
             ->outIs(array('METHOD', 'MAGICMETHOD'))
             ->is('static', true)
             ->outIs('NAME')
             ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class Constantnames extends Analyzer {
    public function analyze(): void {
        // with define
        $this->atomIs('Defineconstant')
             ->outIs('NAME')
             ->is('constant', true)
             ->has('noDelimiter');
        $this->prepareQuery();

        // with const
        $this->atomIs('Const')
             ->hasNoClassInterface()
             ->outIs('CONST')
             ->atomIs('Constant')
             ->outIs('NAME')
             ->atomIs('Identifier');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class RedeclaredPhpFunction extends Analyzer {
    public function analyze(): void {
        $functions = array_merge(exakat('phpCore')->getFunctionList(),
                                 exakat('phpExtensions')->getFunctionList(),
                                 );

        $this->atomIs('Function')
             ->fullnspathIs($functions);
        $this->prepareQuery();
    }
}

?>
<?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
declare(strict_types = 1);

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ErrorReportingWithInteger extends Analyzer {
    public function analyze(): void {
        $allowedIntegers = array('-1', '0');

        $this->atomFunctionIs('\\error_reporting')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(array('Integer', 'Addition'))
             ->codeIsNot($allowedIntegers)
             ->back('first');
        $this->prepareQuery();

        $this->atomFunctionIs('\\ini_set')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->tokenIsNot('T_QUOTE')
             ->noDelimiterIs('error_reporting')
             ->inIs('ARGUMENT')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs('Integer')
             ->codeIsNot('0')
             ->codeIsNot($allowedIntegers)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoDirectAccess extends Analyzer {
    public function analyze(): void {
        //defined('AJXP_EXEC') or die('Access not allowed'); : Constant used!
        $this->atomIs('Logical')
             ->tokenIs(array('T_BOOLEAN_AND', 'T_BOOLEAN_OR', 'T_LOGICAL_AND', 'T_LOGICAL_OR'))
             // find !defined and defined
             ->atomInsideNoDefinition('Functioncall')
             ->functioncallIs('\\defined')
             ->back('first')
             ->atomInsideNoDefinition('Exit')
             ->back('first');
        $this->prepareQuery();

        //if(!defined('CMS')) die/exit
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             // find !defined and defined
             ->atomIs('Not')
             ->outIs('NOT')
             ->atomIs('Functioncall')
             ->functioncallIs('\\defined')
             ->back('first')
             ->outIs('THEN')
             ->atomInsideNoDefinition('Exit')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             // find !defined and defined
             ->atomIs('Functioncall')
             ->functioncallIs('\\defined')
             ->back('first')
             ->outIs('THEN')
             ->atomInsideNoDefinition('Exit')
             ->back('first');
        $this->prepareQuery();

        //if (defined('_ECRIRE_INC_VERSION')) return;
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->atomIs('Functioncall')
             ->functioncallIs('\\defined')
             ->back('first')
             ->outIs('THEN')
             ->outWithRank('EXPRESSION', 0)
             ->atomIs('Return')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->atomIs('Not')
             ->outIs('NOT')
             ->atomIs('Functioncall')
             ->functioncallIs('\\defined')
             ->back('first')
             ->outIs('THEN')
             ->outWithRank('EXPRESSION', 0)
             ->atomIs('Return')
             ->back('first');
        $this->prepareQuery();

        //if (stristr($_SERVER['REQUEST_URI'], ".inc.php")) die("no access");
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->functioncallIs(array('\\stristr', '\\strstr'))
             ->back('first')
             ->outIs('THEN')
             ->outWithRank('EXPRESSION', 0)
             ->atomIs('Exit')
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Files;

use Exakat\Analyzer\Analyzer;

class IsCliScript extends Analyzer {
    public function analyze(): void {
        // files that starts with #!/binary
        $this->atomIs('File')
             ->outIs('FILE')
             ->atomIs('Sequence')
             ->outWithRank('EXPRESSION', 0)
             ->tokenIs('T_INLINE_HTML')
             ->regexIs('fullcode', '^#!')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ForgottenWhiteSpace extends Analyzer {
    public function analyze(): void {
        // spot the first element
        $this->atomIs('File')
             ->outIs('FILE')
             ->outWithRank('EXPRESSION', 0)
             ->regexIs('fullcode', '^\\\s+\\$');
        $this->prepareQuery();

        // Spot the last element
        $this->atomIs('File')
             ->outIs('FILE')
             ->outWithRank('EXPRESSION', 'last')
             ->regexIs('fullcode', '^\\\s+\\$');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class Noscream extends Analyzer {
    protected $authorizedFunctions = 'noscream_functions.json';

    public function analyze(): void {
        $list = array('Addition',
                      'Array',
                      'Arrayappend',
                      'Arrayliteral',
                      //'Assignation',  Not possible
                      'Bitshift',
                      'Boolean',
                      'Break',
                      'Cast',
                      'Clone',
                      'Closure',
                      'Coalesce',
                      'Comparison',
                      'Concatenation',
                      'Constant',
                      'Continue',
                      'Declare',
                      'Declaredefinition',
                      'Defineconstant',
                      'Echo',
                      'Empty',
                      'Eval',
                      'Exit',
                      'Function',
                      'Global',
                      'Heredoc',
                      'Identifier',
                      'Include',
                      'Instanceof',
                      'Insteadof',
                      'Integer',
                      'Isset',
                      'List',
                      //'Logical', Not possible
                      'Magicconstant',
                      'Member',
                      'Methodcall',
                      'Methodcallname',
                      'Multiplication',
                      'Name',
                      'New',
                      'Newcall',
                      'Not',
                      'Nsname',
                      'Null',
                      'Parent',
                      'Parenthesis',
                      'Phpvariable',
                      'Postplusplus',
                      'Power',
                      'Preplusplus',
                      'Print',
                      'Propertydefinition',
                      'Float',
                      'Return',
                      'Self',
                      'Shell',
                      'Sign',
                      'Static',
                      'Staticclass',
                      'Staticconstant',
                      'Staticdefinition',
                      'Staticmethodcall',
                      'Staticproperty',
                      'String',
                      'This',
                      'Throw',
                      'Unset',
                      'Variable',
                      'Yield',
                      'Yieldfrom',
                      );

        // @$s
        $this->atomIs($list)
             ->is('noscream', true);
        $this->prepareQuery();

        // @fopen($s, 'r')
        $this->atomIs('Functioncall')
             ->fullnspathIsNot(makeFullNsPath($this->authorizedFunctions))
             ->is('noscream', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NotNot extends Analyzer {
    public function analyze(): void {
        // !!
        $this->atomIs('Not')
             ->hasNoIn('NOT')
             ->outIs('NOT')
             ->outIsIE('CODE')  // Parenthesis
             ->atomIs('Not')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class StrposCompare extends Analyzer {
    public function analyze(): void {
        $operator = $this->loadIni('php_may_return_boolean_or_zero.ini', 'functions');
        $fullnspaths = makeFullNsPath($operator);

        // if (.. == strpos(..)) {}
        $this->atomFunctionIs($fullnspaths)
             ->inIs('RIGHT')
             ->atomIs('Comparison')
             ->codeIs(array('==', '!='))
             ->outIs('LEFT')
             ->codeIs(array('0', "''", '""', 'null', 'false'))
             ->back('first')
             ->not(
                $this->side()
                     ->fullnspathIs('\preg_match')
                     ->outWithRank('ARGUMENT', 0)
                     ->not(
                        $this->side()
                             ->outIs('CONCAT')
                             ->atomIs(array('Variable', 'Array', 'Member', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
                     )
             );
        $this->prepareQuery();

        // if (strpos(..) == ..) {}
        $this->atomFunctionIs($fullnspaths)
             ->inIs('LEFT')
             ->atomIs('Comparison')
             ->codeIs(array('==', '!='))
             ->outIs('RIGHT')
             ->codeIs(array('0', "''", '""', 'null', 'false'))
             ->back('first')
             ->not(
                $this->side()
                     ->fullnspathIs('\preg_match')
                     ->outWithRank('ARGUMENT', 0)
                     ->not(
                        $this->side()
                             ->outIs('CONCAT')
                             ->atomIs(array('Variable', 'Array', 'Member', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
                     )
             );
        $this->prepareQuery();

        // if (strpos(..)) {}
        $this->atomFunctionIs($fullnspaths)
             ->inIsIE('CODE')  // parenthesis
             ->inIs('CONDITION')
             ->atomIs(array('Ifthen', 'While', 'Dowhile'))
             ->back('first')
             ->not(
                $this->side()
                     ->fullnspathIs('\preg_match')
                     ->outWithRank('ARGUMENT', 0)
                     ->not(
                        $this->side()
                             ->outIs('CONCAT')
                             ->atomIs(array('Variable', 'Array', 'Member', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
                     )
             );
        $this->prepareQuery();

        // if ($x = strpos(..)) {}
        $this->atomFunctionIs($fullnspaths)
             ->inIs('RIGHT')
             ->atomIs('Assignation')
             ->inIsIE('CODE')  // parenthesis
             ->inIs('CONDITION')
             ->atomIs(array('Ifthen', 'While', 'Dowhile'))
             ->back('first')
             ->not(
                $this->side()
                     ->fullnspathIs('\preg_match')
                     ->outWithRank('ARGUMENT', 0)
                     ->not(
                        $this->side()
                             ->outIs('CONCAT')
                             ->atomIs(array('Variable', 'Array', 'Member', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
                     )
             );
        $this->prepareQuery();

        // if (($x = strpos(..)) == false) {}
        $this->atomFunctionIs($fullnspaths)
             ->inIs('RIGHT')
             ->as('result')
             ->atomIs('Assignation')
             ->inIs('CODE')
             ->inIs(array('RIGHT', 'LEFT'))
             ->atomIs('Comparison')
             ->outIs(array('RIGHT', 'LEFT'))
             ->codeIs(array('0', "''", '""', 'null', 'false'))
             ->inIs(array('RIGHT', 'LEFT'))
             ->codeIs(array('==', '!='))
             ->inIs('CONDITION')
             ->atomIs(array('Ifthen', 'While', 'Dowhile'))
             ->back('first')
             ->not(
                $this->side()
                     ->fullnspathIs('\preg_match')
                     ->outWithRank('ARGUMENT', 0)
                     ->not(
                        $this->side()
                             ->outIs('CONCAT')
                             ->atomIs(array('Variable', 'Array', 'Member', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
                     )
             );
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ThrowsAndAssign extends Analyzer {
    public function analyze(): void {
        // throw $e = new Exception();
        $this->atomIs('Throw')
             ->outIs('THROW')
             ->atomIs('Assignation')
             ->outIs('LEFT')
             ->inIs('DEFINITION')
             ->atomIs('Variabledefinition') // if property, then it may be reused
             ->raw('where(__.out("DEFINITION").count().is(eq(1)))')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class VardumpUsage extends Analyzer {
    public function analyze(): void {
        $debugFunctions       = array('var_dump', 'print_r', 'var_export');
        $returnDebugFunctions = array('\\print_r', '\\var_export');

        // print_r (but not print_r($a, 1))
        $this->atomFunctionIs($debugFunctions)
             ->outWithRank('ARGUMENT', 1)
             ->is('boolean', false)
             ->atomIsNot(self::CONTAINERS)
             ->back('first');
        $this->prepareQuery();

        $this->atomFunctionIs('\\var_dump')
             ->back('first');
        $this->prepareQuery();

        // (well, we need to check if the result string is not printed now...)
        $this->atomFunctionIs($returnDebugFunctions)
             ->noChildWithRank('ARGUMENT', 1)
             ->back('first');
        $this->prepareQuery();

        // echo '<pre>'.print_r($a, 1);
        $this->atomIs(array('Echo', 'Print'))
             ->atomInsideNoDefinition('Functioncall')
             ->functioncallIs($returnDebugFunctions)
             ->back('first');
        $this->prepareQuery();

//         call_user_func_array('var_dump', )
        $this->atomIs('Functioncall')
             ->functioncallIs(array('\\call_user_func_array', '\\call_user_func'))
             ->outWithRank('ARGUMENT', 0)
             ->noDelimiterIs($debugFunctions)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassRemoteDefinitionWithTypehint extends Complete {
    public function analyze(): void {

        // function bar(A $a) { $a->foo()}; class A { function foo() {}}
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('OBJECT')
              ->atomIs('Variableobject')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              ->atomIs(array('Class', 'Classanonymous'))
              // No check on Atom == Class, as it may not exists
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->atomIs(array('Class', 'Classanonymous', 'Trait'))
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // class B { public A $p; function bar() { $this->a->foo()}; class A { function foo() {}}
        $this->atomIs(array('Methodcall', 'Staticmethodcall'), self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs(array('OBJECT', 'CLASS'))
              ->atomIs(array('Member', 'Staticproperty'))
              ->inIs('DEFINITION')
              ->inIs('PPP')
              ->outIs('TYPEHINT')
              ->atomIs(array('Identifier', 'Nsname'))
              ->inIs('DEFINITION')
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->atomIs(array('Class', 'Classanonymous'))
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // function bar(A $a) { $a->p}; class A { public $p;}
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->atomIs('Variableobject')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->savePropertyAs('fullnspath', 'fnp')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->notSamePropertyAs('fullnspath', 'fnp')
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();

        // class A { private $p; function bar(A $a) { $a->p} } special case
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->atomIs('Variableobject')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->savePropertyAs('fullnspath', 'fnp')
              ->inIs('DEFINITION')
              // No check on interface here
              // No check on Atom == Class, as it may not exists
              ->samePropertyAs('fullnspath', 'fnp')
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();

        // class B { public A $p; function bar() { $this->a->p2()}; class A { public $p2;}
        $this->atomIs(array('Member', 'Staticproperty'), self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs(array('OBJECT', 'CLASS'))
              ->atomIs(array('Member', 'Staticproperty'))
              ->inIs('DEFINITION')
              ->inIs('PPP')
              ->outIs('TYPEHINT')
              ->atomIs(array('Identifier', 'Nsname'))
              ->inIs('DEFINITION')
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->raw('filter{ it.get().value("propertyname") == name || it.get().value("code") == name;}')
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();

        // function bar(A $a) { $a::C}; class A { const C = 1;}
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->as('constante')
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('CONSTANT')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              ->atomIs(array('Class', 'Trait'), self::WITHOUT_CONSTANTS)
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'constante');
        $this->prepareQuery();

        // class B { public A $p; function bar() { $this->a::C}; class A { const C = 1;}
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->as('constante')
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('CONSTANT')
              ->outIs('CLASS')
              ->atomIs(array('Member', 'Staticproperty'))
              ->inIs('DEFINITION')
              ->inIs('PPP')
              ->outIs('TYPEHINT')
              ->atomIs(array('Identifier', 'Nsname'))
              ->inIs('DEFINITION')
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'constante');
        $this->prepareQuery();

        // Create link between static Class method and its definition
        // This works outside a class too, for static.
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs('Variable', self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->isNot('visibility', 'private')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class MultipleConstantDefinition extends Analyzer {
    public function analyze(): void {
        // case-insensitive constants with Define
        // Search for definitions and count them
        //define()
        $this->atomIs('Defineconstant')
             ->raw('or( __.out("CASE").count().is(eq(0)),
          __.out("CASE").has("boolean", false))')
             ->outIs('NAME')
             ->atomIs(self::STATIC_NAMES)
             ->values('noDelimiter');
        $csDefinitions = $this->rawQuery()->toArray();

        //const
        $this->atomIs('Const')
             ->hasNoClassTrait()
             ->outIs('CONST')
             ->outIs('NAME')
             ->values('fullcode');
        $constDefinitions = $this->rawQuery()->toArray();

        //define(, , true)
        $this->atomIs('Defineconstant')
             ->outIs('CASE')
             ->is('boolean', true)
             ->back('first')
             ->outIs('NAME')
             ->atomIs(self::STATIC_NAMES)
             ->values('noDelimiter');
        $cisDefinitions = $this->rawQuery()->toArray();
        $cisDefinitions = array_map('strtolower', $cisDefinitions);

        if ($a = $this->selfCollisions($cisDefinitions)) {
            $this->applyToCisDefine($a);
        }

        if ($a = $this->selfCollisions(array_merge($constDefinitions, $csDefinitions))) {
            $this->applyToConst(array_intersect($a, $constDefinitions));
            $this->applyToCsDefine(array_intersect($a, $csDefinitions));
        }

        if ($a = $this->CsCisCollisions($csDefinitions, $cisDefinitions)) {
            $this->applyToCisDefine($a);
            $this->applyToCsDefine($a);
        }

        if ($a = $this->CsCisCollisions($constDefinitions, $cisDefinitions)) {
            $this->applyToCisDefine($a);
            $this->applyToConst($a);
        }
    }

    private function selfCollisions(array $array): array {
        // two definitions are case sensitive
        return array_keys(array_filter(array_count_values($array), function (int $x): bool { return $x > 1; }));
    }

    private function CsCisCollisions(array $csDefinitions, array $cisDefinitions): array {
        return array_merge( array_intersect($csDefinitions, $cisDefinitions),
                            array_intersect($csDefinitions, array_map(function (string $x): string { return strtoupper($x); }, $cisDefinitions) ) );
    }

    private function applyToCisDefine(array $array): void {
        if (empty($array)) {
            return;
        }
        $array = array_values($array);

        $this->atomIs('Defineconstant')
             ->outIs('CASE')
             ->is('boolean', true)
             ->inIs('CASE')
             ->outIs('NAME')
             ->atomIs('Identifier')
             ->hasNoOut('CONCAT')
             ->noDelimiterIs($array);
        $this->prepareQuery();
    }

    private function applyToCsDefine(array $array): void {
        if (empty($array)) {
            return;
        }
        $array = array_values($array);

        $this->atomIs('Defineconstant')
             ->outIs('CASE')
             ->is('boolean', false)
             ->inIs('CASE')
             ->outIs('NAME')
             ->atomIs('Identifier')
             ->hasNoOut('CONCAT')
             ->noDelimiterIs($array);
        $this->prepareQuery();

        $this->atomIs('Defineconstant')
             ->hasNoOut('CASE')
             ->outIs('NAME')
             ->atomIs('Identifier')
             ->hasNoOut('CONCAT')
             ->noDelimiterIs($array);
        $this->prepareQuery();
    }

    private function applyToConst(array $array): void {
        if (empty($array)) {
            return;
        }
        $array = array_values($array);

        $this->atomIs('Const')
             ->hasNoClassTrait()
             ->outIs('CONST')
             ->outIs('NAME')
             ->codeIs($array);
        $this->prepareQuery();
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class WrongOptionalParameter extends Analyzer {
    public function analyze(): void {
        // function foo($a, $b = 2, $c) {}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('x')
             ->outIs('DEFAULT')
             ->atomIsNot('Void')
             ->hasNoIn('RIGHT')

             ->back('x')
            // with typehint, but nullable (so, optional)
            // valid since PHP 8.0
             ->not(
                $this->side()
                     ->outIs('TYPEHINT')
                     ->atomIsNot(array('Void', 'Null'))
                     ->inIs('TYPEHINT')
                     ->outIs('DEFAULT')
                     ->atomIs('Null')
                     ->hasNoIn('RIGHT')
             )

             ->nextSibling('ARGUMENT')
             ->outIsIE('PPP')
             ->isNot('variadic', true)
             ->outIs('DEFAULT')
             ->atomIs('Void')
             ->hasNoIn('RIGHT')

             ->back('first');
        $this->prepareQuery();

        // function __construct($a, $b = 2, $c) {}
        $this->atomIs('Magicmethod')
             ->outIs('ARGUMENT')
             ->atomIs('Ppp')
             ->as('x')
             ->outIs('PPP')
             ->outIs('DEFAULT')
             ->atomIsNot('Void')
             ->hasNoIn('RIGHT')

             ->back('x')
            // with typehint, but nullable (so, optional)
            // valid since PHP 8.0
             ->not(
                $this->side()
                     ->outIs('TYPEHINT')
                     ->atomIsNot(array('Void', 'Null'))
                     ->inIs('TYPEHINT')
                     ->outIs('PPP')
                     ->outIs('DEFAULT')
                     ->atomIs('Null')
                     ->hasNoIn('RIGHT')
             )

             ->nextSibling('ARGUMENT')
             // case of promoted properties
             ->outIsIE('PPP')
             ->outIs('DEFAULT')
             ->atomIs('Void')
             ->hasNoIn('RIGHT')

             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class IsnullVsEqualNull extends Analyzer {
    public function analyze(): void {
        $this->atomFunctionIs('\\is_null');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Type;

use Exakat\Analyzer\Analyzer;

class OneVariableStrings extends Analyzer {
    public function analyze(): void {
        // "$x"
        $this->atomIs('String')
             ->is('count', 1)
             ->outIs('CONCAT')
             ->atomIs(array('Variable', 'Array', 'Member', 'Methodcall', 'Staticmethodcall'));
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class StaticContainsThis extends Analyzer {

    public function analyze(): void {
        // static function foo() { $this->a ;}
        $this->atomIs('Method')
             ->is('static', true)
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('This')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class WhileListEach extends Analyzer {
    public function analyze(): void {
        // while (list($a, $b) = each($c)) {}
        $this->atomIs('While')
             ->outIs('CONDITION')
             ->atomIs('Assignation')
             ->as('assignation')
             ->outIs('LEFT')
             ->atomIs('List')
             ->back('assignation')
             ->outIs('RIGHT')
             ->functioncallIs('\\each')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class MultipleDefinedCase extends Analyzer {
    public function analyze(): void {
        // Check that fullcode is the same or not for integers
        $this->atomIs(self::SWITCH_ALL)
             ->filter(
                $this->side()
                     ->outIs('CASES')
                     ->outIs('EXPRESSION')
                     ->atomIs('Case')
                     ->outIs('CASE')
                     ->atomIs(array('Integer', 'Null', 'Boolean'), self::WITH_CONSTANTS)
                     ->raw('groupCount().by("intval").map{ it.get().findAll{ it.value > 1}.size()}.is(gte(1))')
             );
        $this->prepareQuery();

        // Special case for strings (avoiding ' and ")
        $this->atomIs(self::SWITCH_ALL)
             ->analyzerIsNot('self')
             ->filter(
                $this->side()
                     ->outIs('CASES')
                     ->outIs('EXPRESSION')
                     ->atomIs('Case')
                     ->outIs('CASE')
                     ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
                     ->has('noDelimiter')
                     ->raw('groupCount().by("noDelimiter").map{ it.get().findAll{ it.value > 1}.size()}.is(gte(1))')
             );
        $this->prepareQuery();

        // Check that fullcode is the same or not for constants, based on fullnspath
        $this->atomIs(self::SWITCH_ALL)
             ->analyzerIsNot('self')
             ->filter(
                $this->side()
                     ->outIs('CASES')
                     ->outIs('EXPRESSION')
                     ->atomIs('Case')
                     ->outIs('CASE')
                     ->atomIs(array('Nsname', 'Identifier'), self::WITHOUT_CONSTANTS)
                     ->raw('groupCount().by("fullnspath").map{ it.get().findAll{ it.value > 1}.size()}.is(gte(1))')
             );
        $this->prepareQuery();

        // Check that fullcode which are expressions  $a == 1
        $this->atomIs(self::SWITCH_ALL)
             ->analyzerIsNot('self')
             ->filter(
                $this->side()
                     ->outIs('CASES')
                     ->outIs('EXPRESSION')
                     ->atomIs('Case')
                     ->outIs('CASE')
                     ->atomIsNot(array('Nsname', 'Identifier', 'Integer', 'Null', 'Boolean', 'String', 'Concatenation', 'Heredoc' ), self::WITHOUT_CONSTANTS)
                     ->raw('groupCount().by("fullcode").map{ it.get().findAll{ it.value > 1}.size()}.is(gte(1))')
             );
        $this->prepareQuery();

        // Special case for mix of strings and constants
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class SwitchWithoutDefault extends Analyzer {
    public function analyze(): void {
        // switch($x) { case 4 : break; }
        // match($x) { 4 => 1, };
        $this->atomIs(self::SWITCH_ALL)
             ->not(
                $this->side()
                     ->outIs('CASES')
                     ->outIs('EXPRESSION')
                     ->atomIs('Default')
             );
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NestedTernary extends Analyzer {
    public function analyze(): void {
        //$a ? $b : $c ? $d : $e
        $this->atomIs('Ternary')
             ->outIs(array('THEN', 'ELSE'))
             ->outIsIE('CODE') // for parenthesis
             ->atomIs('Ternary')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetStringMethodDefinition extends Complete {
    public function analyze(): void {
        // $a = 'B::C' with class B { function C() {}}
        $this->atomIs('String', self::WITHOUT_CONSTANTS)
              ->hasIn('DEFINITION')
              ->regexIs('noDelimiter', '::')
              ->initVariable('name', '""')
              ->raw(<<<'GREMLIN'
filter{ 
    name = it.get().value("noDelimiter").split("::"); 
    if (name.length > 1) {
        name = name[1].toLowerCase();
    } else {
        name = false;
    }
    name != true;
}
GREMLIN
)
              ->inIs('DEFINITION')
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->samePropertyAs('fullcode', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Link to the actual method
        $this->atomIs('String', self::WITHOUT_CONSTANTS)
             ->inIs('DEFINITION')
             ->atomIs(array('Method', 'Magicmethod'))
             ->as('method')
             ->back('first')

             ->inIs('NAME')
             ->atomIs('Functioncall')
             ->addEFrom('DEFINITION', 'method');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class Htmlentitiescall extends Analyzer {
    public function analyze(): void {
        $html_functions = array('\\htmlentities',
                                '\\htmlspecialchars',
                               );

        // Case with no 2nd argument (using default)
        $this->atomFunctionIs($html_functions)
             ->noChildWithRank('ARGUMENT', 1)
             ->back('first');
        $this->prepareQuery();

        // Case with no 3rd argument (using default)
        $this->atomFunctionIs($html_functions)
             ->hasChildWithRank('ARGUMENT', 1)
             ->noChildWithRank('ARGUMENT', 2)
             ->back('first');
        $this->prepareQuery();

        $constants = $this->loadIni('htmlentities_constants.ini', 'constants');
        $constants = makeFullNsPath($constants, \FNP_CONSTANT);

        // Case 2nd argument is a constant
        $this->atomFunctionIs($html_functions)
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(array('Identifier', 'Nsname'))
             ->hasChildWithRank('ARGUMENT', 2)
             ->fullnspathIsNot($constants)
             ->back('first');
        $this->prepareQuery();

        // Case 2nd argument is a combinaison
        $this->atomFunctionIs($html_functions)
             ->hasChildWithRank('ARGUMENT', 2)
             ->outWithRank('ARGUMENT', 1)
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->atomIs('Bitoperation')
             ->outIsIE(array('LEFT', 'RIGHT', 'CODE'))
             ->atomIs(array('Identifier', 'Nsname'))
             ->fullnspathIsNot($constants, self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // Case 3rd argument is one of the following value
        $htmlentities_constants = $this->loadIni('htmlentities_constants.ini', 'encoding');
        $this->atomFunctionIs($html_functions)
             ->hasChildWithRank('ARGUMENT', 2)
             ->outWithRank('ARGUMENT', 2)
             ->atomIs('String')
             ->noDelimiterIsNot($htmlentities_constants, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;


class OverwrittenConstants extends Complete {
    public function analyze(): void {
        // class x { protected const X = 1;}
        // class xx extends x {  protected const X = 1;}
        $this->atomIs('Constant', self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->savePropertyAs('code', 'name')
              ->goToClassInterface()
              ->goToAllImplements(self::EXCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->atomIs('Constant', self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->samePropertyAs('code', 'name',  self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Common\ClassUsage as CommonClassUsage;

class ClassUsage extends CommonClassUsage {
    public function analyze(): void {
        // Empty array to handle ALL classes
        $this->classes = array();

        parent::analyze();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Composer;

use Exakat\Analyzer\Analyzer;
use Exakat\Data\Composer;

class IsComposerNsname extends Analyzer {
    public function analyze(): void {
        $data = new Composer($this->config);

        $packagistNamespaces = $data->getComposerNamespaces();
        $packagistNamespacesFullNS = makeFullNsPath($packagistNamespaces);

        $packagistClasses = $data->getComposerClasses();
        $packagistClassesFullNS = makeFullNsPath($packagistClasses);

        $packagistInterfaces = $data->getComposerInterfaces();
        $packagistInterfacesFullNs = makeFullNsPath($packagistInterfaces);

        $packagistTraits = $data->getComposerTraits();
        $packagistTraitsFullNs = makeFullNsPath($packagistTraits);

        ////////////////////////////////////////////////
        // Use
        // namespaces in Composer
        $list = array_merge($packagistNamespacesFullNS, $packagistClassesFullNS, $packagistInterfacesFullNs, $packagistTraitsFullNs);
        $n = floor(count($list) / 10000);
        for($i = 0; $i < $n; ++$i) {
            $this->atomIs('Usenamespace')
                 ->outIs('USE')
                 ->is('origin', array_slice($list, $i * 10000, ($i + 1) * 10000))
                 ->analyzerIsNot('self');
            $this->prepareQuery();
        }

        ////////////////////////////////////////////////
        // Classes extends or implements
        // Classes in Composer
        $list = array_merge($packagistInterfacesFullNs, $packagistClassesFullNS);
        $n = floor(count($list) / 10000);
        for($i = 0; $i < $n; ++$i) {
            $this->atomIs('Class')
                 ->outIs(array('IMPLEMENTS', 'EXTENDS'))
                 ->fullnspathIs(array_slice($list, $i * 10000, ($i + 1) * 10000))
                 ->analyzerIsNot('self');
            $this->prepareQuery();
        }

        ////////////////////////////////////////////////
        // Instanceof
        // Classes or interfaces in Composer
        $n = floor(count($packagistInterfacesFullNs) / 10000);
        for($i = 0; $i < $n; ++$i) {
            $this->atomIs('Instanceof')
                 ->outIs('CLASS')
                 ->atomIs(array('Nsname', 'Identifier'))
                 ->tokenIsNot('T_STATIC')
                 ->analyzerIsNot('self')
                 ->fullnspathIs(array_slice($packagistInterfacesFullNs, $i * 10000, ($i + 1) * 10000))
                 ->analyzerIsNot('self');
            $this->prepareQuery();
        }

    }
}

?>
<?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
declare(strict_types = 1);

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class IsExtFunction extends Analyzer {

    public function analyze(): void {
        // substr('abc', 0, 1)
        $this->atomIs('Functioncall')
             ->tokenIs(self::STATICCALL_TOKEN)
             ->hasNoIn('DEFINITION')
             ->is('isExt', true);
        $this->prepareQuery();

        // isset($a)
        $this->atomIs(array('Isset', 'Isset', 'Empty', 'Unset', 'Exit', 'Empty', 'Echo', 'Print'));
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Interfaces;

use Exakat\Analyzer\Common\InterfaceUsage as CommonInterfaceUsage;

class InterfaceUsage extends CommonInterfaceUsage {

    public function analyze(): void {
        $this->interfaces = array();

        parent::analyze();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Traits;

use Exakat\Analyzer\Common\TraitUsage as CommonTraitUsage;

class TraitUsage extends CommonTraitUsage {

    public function analyze(): void {
        // class x { use trait; }
        $this->atomIs(self::CLASSES_TRAITS)
             ->outIs('USE')
             ->atomIs('Usetrait');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Namespaces;

use Exakat\Analyzer\Analyzer;

class NamespaceUsage extends Analyzer {

    public function analyze(): void {
        $this->atomIs('Namespace');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Common\UsedDirective;

class DirectivesUsage extends UsedDirective {
    public function analyze(): void {
        $this->directives = (array) $this->loadIni('php_directives.ini', 'directives');

        parent::analyze();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class DanglingArrayReferences extends Analyzer {
    public function analyze(): void {
        //foreach($a as &$b) {}
        // No following unset()
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->is('reference', true)
             ->savePropertyAs('code', 'array')
             ->back('first')
             ->nextSibling()

            // is it unset($x); ?
            ->not(
                $this->side()
                     ->atomIs('Unset')
                     ->outIs('ARGUMENT')
                     ->samePropertyAs('code', 'array')
            )

            // is is (unset) $x;?
            ->not(
                $this->side()
                     ->atomIs('Cast')
                     ->tokenIs('T_UNSET_CAST')
                     ->outIs('CAST')
                     ->samePropertyAs('code', 'array')
            )

            ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class AliasesUsage extends Analyzer {

    public function analyze(): void {
        // sizeof();
        $ini = $this->load('aliases', 'alias');
        $ini = makeFullNsPath(array_keys($ini));

        $this->atomFunctionIs($ini);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class UsesDefaultArguments extends Analyzer {
    // $c = count($x);
    //PHP native function : count($array, $options)
    public function analyze(): void {
        $functions = $this->methods->getFunctionsArgsInterval();

        $positions = array();
        foreach($functions as $function) {
            if ($function['args_min'] === $function['args_max']) { continue; }
            if ($function['args_max'] === \MAX_ARGS) { continue; }
            // Only test if the last is missing. This is sufficient
            $positions["\\$function[name]"] = $function['args_max'];
        }

        $this->atomFunctionIs(array_keys($positions))
             ->savePropertyAs('fullnspath', 'fnp')
             ->isLessHash('count', $positions, 'fnp');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class OverwrittenMethods extends Complete {
    public function analyze(): void {

        // This query is more specific than the next
        // class x {use t { t::a as b}}
        $this->atomIs('Virtualmethod', self::WITHOUT_CONSTANTS)
             ->hasNoOut('OVERWRITE')
             ->savePropertyAs('lccode', 'vname')
             ->goToClass()
             ->as('class')

             ->outIs('USE')
             ->outIs('BLOCK')
             ->outIs('EXPRESSION')
             ->atomIs('As')
             ->outIs('AS')
             ->samePropertyAs('code', 'vname',  self::CASE_INSENSITIVE)
             ->inIs('AS')
             ->outIs('NAME')
             ->outIs('METHOD')
             ->savePropertyAs('lccode', 'name')
             ->inIs('METHOD')
             ->outIs('CLASS')
             ->inIs('DEFINITION')
             ->atomIs('Trait')

             ->outIs(array('METHOD', 'MAGICMETHOD'))
             ->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS) // No virtualmethod here
             ->as('origin')
             ->outIs('NAME')
             ->samePropertyAs('code', 'name',  self::CASE_INSENSITIVE)
             ->back('origin')
             ->dedup(array('first', 'origin'))
             ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();

        // class x {use t { a as b}}
        $this->atomIs('Virtualmethod', self::WITHOUT_CONSTANTS)
             ->hasNoOut('OVERWRITE')
             ->savePropertyAs('lccode', 'vname')
             ->goToClass()
             ->as('class')

             ->outIs('USE')
             ->outIs('BLOCK')
             ->outIs('EXPRESSION')
             ->atomIs('As')
             ->outIs('AS')
             ->samePropertyAs('code', 'vname',  self::CASE_INSENSITIVE)
             ->inIs('AS')
             ->outIs('NAME')
             ->savePropertyAs('lccode', 'name')
             ->back('class')

             ->goToAllParentsTraits(self::EXCLUDE_SELF)
             ->outIs(array('METHOD', 'MAGICMETHOD'))
             ->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS) // No virtualmethod here
             ->as('origin')
             ->outIs('NAME')
             ->hasNoOut('METHOD')
             ->samePropertyAs('code', 'name',  self::CASE_INSENSITIVE)
             ->back('origin')
             ->dedup(array('first', 'origin'))
             ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();

        // class x {use t}
        $this->atomIs('Virtualmethod', self::WITHOUT_CONSTANTS)
             ->hasNoOut('OVERWRITE')
             ->savePropertyAs('lccode', 'name')
             ->goToClass()
             ->as('class')

             ->outIs('USE')
             ->hasNoOut('BLOCK')
             ->back('class')

             ->goToAllParentsTraits(self::EXCLUDE_SELF)
             ->outIs(array('METHOD', 'MAGICMETHOD'))
             ->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS) // No virtualmethod here
             ->as('origin')
             ->outIs('NAME')
             ->hasNoOut('METHOD')
             ->samePropertyAs('code', 'name',  self::CASE_INSENSITIVE)
             ->back('origin')
             ->dedup(array('first', 'origin'))
             ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();

        // This must be second, so it will skip more specific configuration above
        // class x { protected function foo()  {}}
        // class xx extends x { protected function foo()  {}}
        $this->atomIs(array('Method', 'Magicmethod', 'Virtualmethod'), self::WITHOUT_CONSTANTS)
             ->hasNoOut('OVERWRITE')
             ->outIsIE('NAME')
             ->savePropertyAs('lccode', 'name')
             ->goToClass()
             ->as('theClass')
             ->goToAllParents(self::EXCLUDE_SELF)
             ->outIs(array('METHOD', 'MAGICMETHOD'))
             ->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS) // No virtualmethod here
             ->as('origin')
             ->outIs('NAME')
             ->samePropertyAs('code', 'name',  self::CASE_INSENSITIVE)
             ->back('origin')
             ->dedup(array('first', 'origin'))
             ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();

        // interface x { protected function foo()  {}}
        // interface xx extends x { protected function foo()  {}}
        $this->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS)
             ->hasNoOut('OVERWRITE')
             ->outIs('NAME')
             ->savePropertyAs('lccode', 'name')
             ->goToInterface()
             ->goToAllImplements(self::EXCLUDE_SELF)
             ->outIs(array('METHOD', 'MAGICMETHOD'))
             ->outIs('NAME')
             ->samePropertyAs('code', 'name',  self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->as('origin')
             ->dedup(array('first', 'origin'))
             ->addEFrom('OVERWRITE', 'first');
        $this->prepareQuery();

        // relay virtualmethods definitions to the methodcalls
        $this->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS)
             ->inIs('OVERWRITE')
             ->atomIs('Virtualmethod')
             ->outIs('DEFINITION')
             ->as('origin')
             ->dedup(array('first', 'origin'))
             ->addEFrom('DEFINITION', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class VariableArguments extends Analyzer {
    public function analyze(): void {
        // Using function_get_args
        $this->atomFunctionIs(array('\\func_get_arg',
                                    '\\func_get_args',
                                    '\\func_num_args',
                                    ))
             ->goToFunction();
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class CreateCompactVariables extends Complete {
    public function analyze(): void {
        // compact('a') : 'a' is one usage of a variable
        $this->atomFunctionIs('\compact')
              ->outIs('ARGUMENT')
              ->has('noDelimiter')
              ->as('varInString')
              ->savePropertyAs('noDelimiter', 'name')
              ->makeVariableName('name')
              ->goToInstruction(array('Function', 'Closure', 'Method', 'Magicmethod', 'File'))
              ->outIs(array('DEFINITION', 'ARGUMENT', 'USE'))
              ->atomIs(array('Variabledefinition', 'Globaldefinition', 'Staticdefinition', 'Parameter'), self::WITHOUT_CONSTANTS)
              ->outIsIE('NAME')
              ->samePropertyAs('fullcode', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'varInString');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class CantUse extends Analyzer {
    public function analyze(): void {
        // Function foo() { throw new exception(); }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Throw')
             ->back('first');
        $this->prepareQuery();

        // Function foo() { trigger_error(); }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->functioncallIs(array('\\trigger_error', '\\user_error'))
             ->back('first');
        $this->prepareQuery();

        // propagation to custom functions
        // Function foo() { trigger_error(); }
        // Function bar() { bar(); }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->inIs('DEFINITION')
             ->analyzerIs('self')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Exceptions;

use Exakat\Analyzer\Analyzer;

class OverwriteException extends Analyzer {
    public function analyze(): void {
        // try { } catch (E $e) { $e = 3;}
        $this->atomIs('Catch')
             ->outIs('VARIABLE')
             ->savePropertyAs('code', 'exception')
             ->inIs('VARIABLE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Variable')
             ->samePropertyAs('code', 'exception')
             ->is('isModified', true)
             // not chained and replaced.
             ->raw(<<<'GREMLIN'
not( 
    __.where( 
        __.in("LEFT")
          .out("RIGHT")
          .out("NEW")
          .out("ARGUMENT")
          .has("rank", 2)
          .filter{ it.get().value("code") == exception; } 
        )
)
GREMLIN
)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class HasMagicProperty extends Analyzer {
    public function analyze(): void {
        $methods = $this->loadIni('php_magic_methods.ini', 'magicMethod');
        $methods = array_diff($methods, array('__construct', '__destruct'));

        // Nsname that is not used somewhere else
        $this->atomIs(self::CLASSES_TRAITS)
             ->outIs('MAGICMETHOD')
             ->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs($methods, self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // Nsname that is not used somewhere else
        $this->atomIs(self::CLASSES_TRAITS)
             ->analyzerIsNot('self')
             ->goToAllParents(self::EXCLUDE_SELF)
             ->analyzerIs('self')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class BooleanStrictComparison extends Analyzer {
    public function analyze(): void {
        // while (list($a, $b) = each($c)) {}
        $this->atomIs('Comparison')
             ->codeIsNot(array('===', '!=='))
             ->outIs(array('RIGHT', 'LEFT'))
             ->atomIs('Boolean')
             ->back('first');
        $this->prepareQuery();

        // in_array ($array, $value);
        $this->atomFunctionIs('\\in_array')
             ->noChildWithRank('ARGUMENT', 2);
        $this->prepareQuery();

        // in_array ($array, $value, false);
        $this->atomFunctionIs('\\in_array')
             ->outWithRank('ARGUMENT', 2)
             ->is('boolean', false)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class LoneBlock extends Analyzer {
    public function analyze(): void {
        // if (1) {{ $b++; }}
        $this->atomIs('Sequence')
             ->is('bracket', true)
             ->hasIn('EXPRESSION');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class LogicalInLetters extends Analyzer {
    public function analyze(): void {
        // $a and $b
        $this->atomIs('Logical')
             ->codeIs(array('and', 'or', 'xor'))
             ->hasNoParent('Parenthesis', array('CODE'));
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class ConstantClass extends Analyzer {
    public function analyze(): void {
        // class x { const yx = 2;}
        $this->atomIs('Class')
             ->isNot('abstract', true)
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outIs(array('METHOD', 'MAGICMETHOD', 'PPP'))
                     )
             )
             ->hasOut('CONST');
        $this->prepareQuery();

        // interface x { const yx = 2;}
        $this->atomIs('Interface')
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outIs(array('METHOD', 'MAGICMETHOD', 'PPP'))
                     )
             )
             ->hasOut('CONST');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class RepeatedPrint extends Analyzer {
    public function analyze(): void {
        // first one in sequence
        $this->atomIs(array('Print', 'Echo'))
             ->is('rank', 0)
             ->nextSibling()
             ->atomIs(array('Print', 'Echo'))
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(array('Print', 'Echo'))
             ->isNot('rank', 0)
             ->nextSibling()
             ->atomIs(array('Print', 'Echo'))
             ->back('first')
             ->previousSibling()
             ->atomIsNot(array('Print', 'Echo'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class PrintWithoutParenthesis extends Analyzer {
    public function analyze(): void {
        // print(1);
        $this->atomIs('Print')
             ->outIs('ARGUMENT')
             ->atomIs('Parenthesis')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ObjectReferences extends Analyzer {
    public function analyze(): void {
        $scalars = $this->loadIni('php_scalar_types.ini', 'types');

        // f(stdclass &$x)
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->is('reference', true)

             // No scalar is requested
             ->not(
                 $this->side()
                      ->outIs('TYPEHINT')
                      ->fullnspathIs($scalars)
                      ->fullnspathIsNot('\null')
             )

            // One of the requested type is an object
             ->outIs('TYPEHINT')
             ->atomIsNot('Void')
             ->fullnspathIsNot($scalars)

             ->back('first');
        $this->prepareQuery();

        // f(&$x) and $x->y();
        // f(&$x) and $x->y;
        // No assignation with new inside
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->is('reference', true)
             ->savePropertyAs('code', 'variable') // Avoid &
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outIs('NAME')
                             ->outIs('DEFINITION')
                             ->inIs('LEFT')
                             ->atomIs('Assignation') // any assignation will break the reference
                )
             )
             ->inIs('ARGUMENT')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition(array('Methodcall', 'Member'))
             ->outIs('OBJECT')
             ->samePropertyAs('code', 'variable')
             ->back('first');
        $this->prepareQuery();

        // foreach($a as &$b) { $b->method;}
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->is('reference', true)
             ->savePropertyAs('code', 'variable')
             ->back('first')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition(array('Methodcall', 'Member'))
             ->outIs('OBJECT')
             ->samePropertyAs('code', 'variable');
        $this->prepareQuery();

        // @todo &function() { return new A; }

        // @todo $x = new object; then &$x;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Type;

use Exakat\Analyzer\Analyzer;

class NoRealComparison extends Analyzer {
    public function analyze(): void {
        // 1.2 == 3.4
        // 1.2 == 3.4 + 0
        $this->atomIs('Comparison')
             ->codeIs(array('==', '!=', '===', '!=='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomInsideNoDefinition(array('Float', 'Identifier', 'Nsname', 'Staticconstant'))
             ->hasNoIn(array('ARGUMENT', 'INDEX'))
             ->atomIs('Float', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class DirectCallToMagicMethod extends Analyzer {
    public function analyze(): void {
        $magicMethods = $this->loadIni('php_magic_methods.ini', 'magicMethod');

        $this->atomIs('Methodcallname')
             ->codeIs($magicMethods)
             ->inIs('METHOD')
             ->not(
                $this->side()
                     ->outIs('CLASS')
                     ->atomIs(self::RELATIVE_CLASS)
             );
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UselessFinal extends Analyzer {
    public function analyze(): void {
        // final class x { final function foo() {}}
        $this->atomIs('Class')
             ->is('final', true)
             ->outIs('METHOD')
             ->atomIs('Method')
             ->is('final', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UseConstant extends Analyzer {
    public function analyze(): void {
        $this->atomFunctionIs(array('\\php_version',
                                    '\\php_sapi_name',
                                    '\\pi',
                                    ))
             ->back('first');
        $this->prepareQuery();

        $this->atomFunctionIs('\\fopen')
             ->outWithRank('ARGUMENT', 0)
             ->noDelimiterIs(array('php://stdin', 'php://stdout', 'php://stderr'))
             ->back('first');
        $this->prepareQuery();

        // dirname(__FILE__) => __DIR__
        $this->atomFunctionIs('\\dirname')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Magicconstant')
             ->codeIs('__file__', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UselessUnset extends Analyzer {
    public function analyze(): void {
        // unset on arguments, reference or value
        $this->atomIs('Unset')
             ->outIs('ARGUMENT')
             ->atomIs('Variable')
             ->isArgument()
             ->back('first');
        $this->prepareQuery();

        // unset on global
        $this->atomIs('Unset')
             ->outIs('ARGUMENT')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'varname')
             ->goToFunction()
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Global')
             ->outIs('GLOBAL')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // unset on static
        $this->atomIs('Unset')
             ->outIs('ARGUMENT')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'varname')
             ->goToFunction()
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Static')
             ->outIs('STATIC')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // unset on foreach (variable or property)
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->atomIs(array('Variable', 'Member'))
             ->outIsIE('OBJECT')
             ->savePropertyAs('code', 'varname')
             ->inIsIE('OBJECT')
             ->inIs('VALUE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Unset')
             ->as('result')
             ->outIs('ARGUMENT')
             ->outIsIE('OBJECT')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->inIsIE('OBJECT')
             ->not(
                $this->side()
                     ->outIs('OBJECT')
                     ->atomIs('Member')
             )
             ->back('result');
        $this->prepareQuery();

        // unset on foreach (KeyVal -> value)
        $this->atomIs('Foreach')
             ->outIs('INDEX')
             ->atomIs(array('Variable', 'Member'))
             ->outIsIE('OBJECT')        // Case it is a property...
             ->savePropertyAs('code', 'varname')
             ->inIsIE('OBJECT')
             ->inIs('VALUE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Unset')
             ->as('result')
             ->outIs('ARGUMENT')
             ->outIsIE('OBJECT')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->inIsIE('OBJECT')
             ->not(
                $this->side()
                     ->outIs('OBJECT')
                     ->atomIs('Member')
             )
             ->back('result');
        $this->prepareQuery();

    // unset as operator
        // unset on arguments, reference or value
        $this->atomIs('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->outIs('CAST')
             ->atomIs('Variable')
             ->isArgument()
             ->back('first');
        $this->prepareQuery();

        // unset on global
        $this->atomIs('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->outIs('CAST')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'varname')
             ->goToFunction()
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Global')
             ->outIs('GLOBAL')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // unset on static
        $this->atomIs('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->outIs('CAST')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'varname')
             ->goToFunction()
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Static')
             ->outIs('STATIC')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // unset on foreach (variable)
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'varname')
             ->inIs('VALUE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->as('result')
             ->outIs('CAST')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->back('result');
        $this->prepareQuery();

        // unset on foreach (KeyVal)
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->atomIs('Keyvalue')
             ->outIs('VALUE')
             ->savePropertyAs('code', 'varname')
             ->inIs('VALUE')
             ->inIs('VALUE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->as('result')
             ->outIs('CAST')
             ->samePropertyAs('code', 'varname', self::CASE_SENSITIVE)
             ->back('result');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Performances;

use Exakat\Analyzer\Analyzer;

class ArrayMergeInLoops extends Analyzer {
    public function analyze(): void {
        $functions = array('\\array_merge',
                           '\\array_merge_recursive',
//                           '\\file_put_contents',
                           );

        // foreach($a as $b) { $c = array_merge($c, $b); };
        $this->atomFunctionIs($functions)
             ->hasLoop()
             ->atomInsideNoDefinition(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'var')
             ->back('first')
             ->inIs('RIGHT')
             ->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('LEFT')
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'var')
             ->goToLoop();
        $this->prepareQuery();

        // with one level of functioncall : foreach($a as $b) { foo($a); }; function foo(c) { return array_merge($c); }
        $this->atomFunctionIs($functions)
             ->hasNoLoop()
             ->hasIn('RETURN')
             ->goToFunction()
             ->outIs('DEFINITION')
             ->goToLoop();
        $this->prepareQuery();

        // with one level of functioncall : array_map($array, 'foo'); function foo(c) { array_merge($c); }
        $this->atomFunctionIs($functions)
             ->hasNoLoop()
             ->goToFunction()
             ->outIs('DEFINITION')
             ->atomIs(self::STRINGS_ALL, self::WITH_CONSTANTS)
             ->inIs('ARGUMENT')
             ->functioncallIs('\\array_map');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UselessParenthesis extends Analyzer {
    // if ( ($condition) )
    public function analyze(): void {
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->atomIs('Parenthesis');
        $this->prepareQuery();

        // clone
        $this->atomIs('Clone')
             ->outIs('CLONE')
             ->atomIs('Parenthesis')
             ->back('first');
        $this->prepareQuery();

        // yield
        $this->atomIs(array('Yield', 'Yieldfrom'))
             ->outIs('YIELD')
             ->atomIs('Parenthesis')
             ->back('first');
        $this->prepareQuery();

        // while
        $this->atomIs('While')
             ->outIs('CONDITION')
             ->atomIs('Parenthesis');
        $this->prepareQuery();

        // dowhile
        $this->atomIs('Dowhile')
             ->outIs('CONDITION')
             ->atomIs('Parenthesis');
        $this->prepareQuery();

        // switch
        $this->atomIs(self::SWITCH_ALL)
             ->outIs('CONDITION')
             ->atomIs('Parenthesis');
        $this->prepareQuery();

        // $y = (1);
        $this->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs('Parenthesis')
             ->not(
                $this->side()
                     ->outIs('CODE')
                     ->atomIs('Logical')
                     ->tokenIs(array('T_LOGICAL_XOR', 'T_LOGICAL_AND', 'T_LOGICAL_OR'))
             );
        $this->prepareQuery();

        // ($y) == (1);
        $this->atomIs('Comparison')
             ->outIs(array('RIGHT', 'LEFT'))
             ->atomIs('Parenthesis')
             ->outIs('CODE')
             ->atomIsNot('Assignation')
             ->inIs('CODE');
        $this->prepareQuery();

        // ($a = $b) == $c : NOT A CASE
        $this->atomIs('Comparison')
             ->outIs('RIGHT')
             ->atomIs('Parenthesis')
             ->outIs('CODE')
             ->atomIs('Assignation')
             ->inIs('CODE');
        $this->prepareQuery();

        // f(($x))
        $this->atomIs('Functioncall')
             ->outIs('ARGUMENT')
             ->atomIs('Parenthesis')
             ->back('first');
        $this->prepareQuery();

        // (expression);
        $this->atomIs('Parenthesis')
             ->hasIn('EXPRESSION');
        $this->prepareQuery();

        // (literal);
        $this->atomIs('Parenthesis')
             ->analyzerIsNot('self')
             ->outIs('CODE')
             ->atomIs(array('Integer', 'Float', 'Boolean', 'Identifier', 'Variable',
                            'Magicconstant', 'Null', 'Functioncall', 'Member', 'Methodcall',
                            'Staticmethodcall', 'Staticconstant', 'Staticproperty'))
             ->back('first');
        $this->prepareQuery();

        //$d = ((($a)+$b)+$c);
        $this->atomIs('Addition')
             ->analyzerIsNot('self')
             ->inIs('CODE')
             ->atomIs('Parenthesis')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs('Addition')
             ->back('first');
        $this->prepareQuery();

        //$d = ((($a)*$b)*$c);
        $this->atomIs('Multiplication')
             ->analyzerIsNot('self')
             ->inIs('CODE')
             ->atomIs('Parenthesis')
             ->as('results')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs('Multiplication')
             ->back('results');
        $this->prepareQuery();

        //function foo($c = (PHP_OS == 1 ? 1 : 2) ){}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->outIs('DEFAULT')
             ->atomIs('Parenthesis')
             ->hasNoIn('RIGHT')
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class UseObjectApi extends Analyzer {
    public function analyze(): void {
        // mysqli_connect();
        $functions = $this->load('function_to_oop', 'function');
        $functions = makeFullNsPath(array_keys($functions));

        $this->atomFunctionIs($functions);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class AlteringForeachWithoutReference extends Analyzer {
    public function analyze(): void {
        // foreach($a as $k => $v) { $a[$k] += 1;}
        $this->atomIs('Foreach')
             ->outIs('SOURCE')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'source')
             ->inIs('SOURCE')

             ->outIs('INDEX')
             ->savePropertyAs('code', 'k')
             ->back('first')

             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Array')
             ->not(
                $this->side()
                     ->inIs('CAST')
                     ->atomIs('Cast')
                     ->tokenIs('T_UNSET_CAST')
             )
             ->not(
                $this->side()
                     ->inIs('ARGUMENT')
                     ->atomIs('Unset')
             )
             ->is('isModified', true)

             ->outIs('VARIABLE')
             ->samePropertyAs('code', 'source')
             ->inIs('VARIABLE')

             ->outIs('INDEX')
             ->samePropertyAs('code', 'k')

             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class UsePathinfo extends Analyzer {
    public function analyze(): void {
        // getting the file extension with explode
        /*
        $temp = explode('.', $config);
        $ext = array_pop($temp);
        */

        $this->atomIs('Assignation')
             ->outIs('RIGHT')
             ->functioncallIs(array('\\explode', '\\split'))

             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->tokenIs('T_CONSTANT_ENCAPSED_STRING') // could be T_VARIABLE, T_QUOTE, T_OBJECT_OPERATOR, T_DOUBLE_COLON
             ->noDelimiterIs('.')
             ->back('first')
             ->outIs('LEFT')
             ->savePropertyAs('code', 'tmpvar')
             ->inIs('LEFT')
             ->nextSibling()
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->functioncallIs('\\array_pop')
             ->outIs('ARGUMENT')
             ->samePropertyAs('code', 'tmpvar')
             ->back('first');
        $this->prepareQuery();

        /*
        $exploded = explode('.', $filename);
        
        if (count($exploded) > 1) {
            $extension = array_pop($exploded);
        }
        */
        $this->atomIs('Assignation')
             ->outIs('RIGHT')
             ->functioncallIs(array('\\explode', '\\split'))
             ->outWithRank('ARGUMENT', 0)
             ->tokenIs('T_CONSTANT_ENCAPSED_STRING') // could be T_VARIABLE, T_QUOTE, T_OBJECT_OPERATOR, T_DOUBLE_COLON
             ->noDelimiterIs('.')
             ->back('first')
             ->outIs('LEFT')
             ->savePropertyAs('code', 'tmpvar')
             ->inIs('LEFT')
             ->nextSibling()
             ->atomIs('Ifthen')
             ->outIs('THEN')
             ->outIs('EXPRESSION')
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->functioncallIs('\\array_pop')
             ->outIs('ARGUMENT')
             ->samePropertyAs('code', 'tmpvar')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoParenthesisForLanguageConstruct extends Analyzer {
    public function analyze(): void {
        // inclusions
        // throw
        // return
        // print, echo
        $this->atomIs(array('Echo', 'Print', 'Include', 'Throw', 'Return'))
             ->outIs(array('ARGUMENT', 'THROW', 'RETURN'))
             ->atomIs('Parenthesis')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class IsPhpConstant extends Analyzer {
    public function analyze(): void {
        // Namespaced constant (\PATHINFO_BASENAME)
        $this->atomIs(self::STATIC_NAMES)
             ->is('isPhp', true);
        $this->prepareQuery();

        // inside Use
        $this->atomIs('Usenamespace')
             ->hasOut('CONST')
             ->outIs('USE')
             ->analyzerIsNot('self')
             ->is('isPhp', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ImpliedIf extends Analyzer {
    public function analyze(): void {
        // defined() or die
        $this->atomIs('Logical')
             ->codeIs(array('or', '||', 'and', '&&'))

             ->outIs('LEFT')
             ->atomIsNot('Assignation')
             ->inIs('LEFT')

             ->inIsIE('CODE')
             ->inIs('EXPRESSION') // Necessary
             ->hasNoParent('For', 'FINAL')
             ->hasNoParent('For', 'INCREMENT')
             ->hasNoParent('For', 'INIT')

             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ShouldChainException extends Analyzer {
    public function analyze(): void {
        // Throw again, but not the caught variable
        $this->atomIs('Catch')
             ->outIs('VARIABLE')
             ->savePropertyAs('code', 'caught')
             ->inIs('VARIABLE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Throw')
             ->outIs('THROW')
             ->outIs('NEW')
             ->not(
                $this->side()
                     ->outIs('ARGUMENT')
                     ->samePropertyAs('code', 'caught')
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Interfaces;

use Exakat\Analyzer\Analyzer;

class IsExtInterface extends Analyzer {
    public function analyze(): void {
        $interfaces = array_merge(exakat('phpCore')->getInterfaceList(),
                                  exakat('phpExtensions')->getInterfaceList(),
                                 );
        if (empty($interfaces)) {
            return;
        }

        $interfaces = makeFullNsPath($interfaces);

        // implements or extends
        $this->atomIs(self::CLASSES_ALL)
             ->outIs(array('IMPLEMENTS', 'EXTENDS'))
             ->is('isExt', true)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs(self::CLASSES_ALL)
             ->outIs(array('IMPLEMENTS', 'EXTENDS'))
             ->is('isPhp', true)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        // instanceof
        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->is('isExt', true)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->is('isPhp', true)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        // Typehints
        $this->atomIs(self::TYPEHINTABLE)
             ->outIs(self::TYPE_LINKS)
             ->is('isExt', true)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs(self::TYPEHINTABLE)
             ->outIs(self::TYPE_LINKS)
             ->is('isPhp', true)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Composer;

use Exakat\Analyzer\Analyzer;
use Exakat\Data\Composer;

class IsComposerClass extends Analyzer {
    public function analyze(): void {
        $data = new Composer($this->config);

        $classes = $data->getComposerClasses();
        $classesFullNP = makeFullNsPath($classes);

        $this->atomIs('Class')
             ->outIs(array('IMPLEMENTS', 'EXTENDS'))
             ->fullnspathIs($classesFullNP);
        $this->prepareQuery();

        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->tokenIs(self::STATICCALL_TOKEN)
             ->atomIsNot('Array')
             ->fullnspathIs($classesFullNP);
        $this->prepareQuery();

        $this->atomIs('Function')
             ->outIs('ARGUMENT')
             ->outIs('TYPEHINT')
             ->fullnspathIs($classesFullNP);
        $this->prepareQuery();

        $this->atomIs('New')
             ->outIs('NEW')
             ->tokenIsNot('T_VARIABLE')
             ->fullnspathIs($classesFullNP);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Security;

use Exakat\Analyzer\Analyzer;

class ShouldUsePreparedStatement extends Analyzer {
    protected $queryMethod = 'query_methods.json';

    public function analyze(): void {
        $functions = array( '\\pg_query',
                            '\\sqlsrv_query',
                            '\\cubrid_query',
                            '\\sqlite_query',
                            '\\sybase_query',
                            '\\ingres_query',
                            '\\pg_send_query',
                            '\\msql_db_query',
                            '\\mysql_db_query',
                            '\\fbsql_db_query',
                            '\\pg_cancel_query',
                            '\\ifx_query',
                            '\\ibase_free_query',
                            '\\dbx_query',
                            '\\maxdb_multi_query',
                            '\\sqlite_array_query',
                            '\\mysqli_slave_query',
                            '\\mysqli_master_query',
                            '\\sqlite_single_query');

        // dynamic type in the code : mysql_query($res, "select ".$a." from table");
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->outWithRank('CONCAT', 0)
             ->regexIsNot('noDelimiter', '(?i)^\\\\s*(FLUSH|ALTER|CREATE|SHOW|DROP|GRANT)')
             ->back('first');
        $this->prepareQuery();

        // method call $someObject->query("select $b") (probably too wide...)
        $this->atomIs('Methodcall')
             ->outIs('METHOD')
             ->codeIs($this->queryMethod, self::TRANSLATE, self::CASE_INSENSITIVE)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->outWithRank('CONCAT', 0)
             ->regexIsNot('noDelimiter', '(?i)^\\\\s*(FLUSH|ALTER|CREATE|SHOW|DROP|GRANT)')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class PrintAndDie extends Analyzer {
    public function analyze(): void {
        // print 'ok'; die();
        $this->atomIs(array('Echo', 'Print'))
             ->nextSibling()
             ->atomIs('Exit')
             ->outIs('ARGUMENT')
             ->atomIs('Void')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UncheckedResources extends Analyzer {
    // fread(fopen('path/to/file', 'r'));
    public function analyze(): void {
        $resourceUsage = $this->loadJson('resource_usage.json');

        $positions = array(0);
        foreach($resourceUsage as $creation => $usage) {
            $creation = makeFullNsPath($creation);
            foreach($positions as $pos) {
                $position = "function$pos";
                if (!isset($usage->{$position})) {
                    continue;
                }
                $functions = makeFullNsPath((array) $usage->{$position});

                //direct usage of the resource :
                // readdir(opendir('uncheckedDir4'));
                $this->atomFunctionIs($creation)
                     ->inIs('ARGUMENT')
                     ->atomIs('Functioncall')
                     ->fullnspathIs($functions);
                $this->prepareQuery();

                // deferred usage of the resource
                //$dir = opendir('uncheckedDir4'); readdir($dir);
                $this->atomFunctionIs($creation)
                     ->inIs('RIGHT')
                     ->atomIs('Assignation')
                     // checked with a if ($resource) or while($resource)
                     ->hasNoIn('CONDITION')
                     ->as('result')
                     ->outIs('LEFT')
                     ->atomIs(self::CONTAINERS)
                     ->savePropertyAs('fullcode', 'tmpvar')
                     ->inIs('LEFT')
                     ->nextSibling()
                     ->atomInsideNoBlock(self::CONTAINERS)
                     ->samePropertyAs('fullcode', 'tmpvar')

                     // checked with a is_resource
                     ->raw('not( where( __.in("ARGUMENT").has("fullnspath", "\\\\is_resource") ) )')
                     // checked with a !$variable
                     ->hasNoIn('NOT')

                     // checked as the condition in a if/then or while
                     ->hasNoParent(array('Ifthen', 'While'), array('CONDITION'))

                     // checked with a $variable &&
                     ->hasNoChildren('Logical', array('LEFT', 'RIGHT'))

                     // checked with a if ($resource == false) or while($resource == false)
                     ->hasNoComparison()

                     ->back('result');
                $this->prepareQuery();
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ElseIfElseif extends Analyzer {
    // if () {} else  if {}
    // but not if () {} elseif {}
    public function analyze(): void {
        $this->atomIs('Ifthen')
             ->outIs('ELSE')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Ifthen')
             ->tokenIsNot('T_ELSEIF');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Common\MultipleDeclarations as CommonMultipleDeclarations;

class MultipleDeclarations extends CommonMultipleDeclarations {
    public function analyze(): void {
        $this->atom = 'Class';

        parent::analyze();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Namespaces;

use Exakat\Analyzer\Analyzer;

class EmptyNamespace extends Analyzer {
    public function analyze(): void {
        // Namespace with only use is empty
        $this->atomIs('Namespace')
             ->outIs('BLOCK')
             ->raw('where(__.out("EXPRESSION").not( hasLabel("Usenamespace", "Void") ).count().is(eq(0)) )')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class CouldUseShortAssignation extends Analyzer {
    public function analyze(): void {
        // Commutative operation : Addition
        $this->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'receiver')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->atomInsideExpression('Addition')
             ->tokenIs('T_PLUS')
             ->hasNoChildren('Arrayliteral', array( array('LEFT', 'RIGHT')) )
             ->outIsIE(array('LEFT', 'RIGHT', 'CODE'))
             ->samePropertyAs('fullcode', 'receiver', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // Commutative operation : Multiplication
        $this->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'receiver')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->atomInsideExpression('Multiplication')
             ->tokenIs('T_STAR')
             ->outIsIE(array('LEFT', 'RIGHT', 'CODE'))
             ->samePropertyAs('fullcode', 'receiver', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // Non-Commutative operation
        $this->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'receiver')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->outIsIE('CODE') // skip ()
             ->codeIs(array('-', '/', '%', '<<', '>>', '**', '&', '^', '|', '??'))
             ->outIs('LEFT')
             ->outIsIE('CODE') // skip ()
             ->samePropertyAs('fullcode', 'receiver', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // Special case for .
        $this->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'receiver')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->atomIs('Concatenation')
             ->outWithRank('CONCAT', 0)
             ->samePropertyAs('fullcode', 'receiver', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Performances;

use Exakat\Analyzer\Analyzer;

class PrePostIncrement extends Analyzer {
    public function analyze(): void {
        $this->atomIs('Postplusplus')
             ->hasNoIn(array('RIGHT', 'ARGUMENT'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class IndicesAreIntOrString extends Analyzer {
    public function analyze(): void {
        // $x[1.2], $x[true], $x[null];
        $this->atomIs('Array')
             ->outIs('INDEX')
             ->atomIs(array('Boolean', 'Null', 'Float'))
             ->back('first');
        $this->prepareQuery();

        // $x['12'] but not $x['012']
        $this->atomIs('Array')
             ->outIs('INDEX')
             ->atomIs('String')
             ->tokenIsNot('T_NUM_STRING')  // a string but a real integer
             ->hasNoOut('CONCAT')
             ->regexIs('noDelimiter', '^[1-9][0-9]*\\$')
             ->back('first');
        $this->prepareQuery();

        // $x[a] and const a = 2.3
        $this->atomIs('Array')
             ->outIs('INDEX')
             ->atomIs(array('Identifier', 'Nsname'))
             ->inIs('DEFINITION')
             ->outIs('VALUE')
             ->atomIs(array('Boolean', 'Float', 'Null', 'Arrayliteral')) // Functioncall is for array
             ->back('first');
        $this->prepareQuery();

        // $x[a::constant] and class a { const constant = 2.3}
        $this->atomIs('Array')
             ->outIs('INDEX')
             ->atomIs('Staticconstant')
             ->inIs('DEFINITION')
             ->outIs('VALUE')
             ->atomIs(array('Boolean', 'Float', 'Null', 'Arrayliteral')) // Functioncall is for array
             ->back('first');
        $this->prepareQuery();

        // $x[a] and define('a', 2.3)
        $this->atomIs('Array')
             ->outIs('INDEX')
             ->atomIs(array('Identifier', 'Nsname'))
             ->inIs('DEFINITION')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(array('Boolean', 'Float', 'Null', 'Arrayliteral'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Type;

use Exakat\Analyzer\Analyzer;

class ShouldTypecast extends Analyzer {
    public function analyze(): void {
        $typeCasting = array('\\floatval',
                             '\\strval',
                             '\\boolval',
                             '\\settype',
                             );

        // $a = intval($b);
        $this->atomFunctionIs('\\intval')
             ->noChildWithRank('ARGUMENT', 1);
        $this->prepareQuery();

        // $a = strval($b);
        $this->atomFunctionIs($typeCasting);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoSubstrOne extends Analyzer {
    public function analyze(): void {
        // Don't use substr($x, $y, 1) but $x[$y];
        $this->atomFunctionIs('\\substr')
             ->outWithRank('ARGUMENT', 2)
             ->is('intval', 1)
             ->back('first');
        $this->prepareQuery();

        // Don't use substr($x, -1) but $x[-1];
        $this->atomFunctionIs('\\substr')
             ->noChildWithRank('ARGUMENT', 2)
             ->outWithRank('ARGUMENT', 1)
             ->is('intval', -1)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UselessBrackets extends Analyzer {
    public function analyze(): void {
        // $a++; {$b++; }
        $this->atomIs('Sequence')
             ->is('bracket', true)
             ->hasNoIn(array('THEN', 'CASES', 'ELSE', 'BLOCK')) ;
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class pregOptionE extends Analyzer {
    public const FETCH_DELIMITER = <<<'GREMLIN'
filter{ 
    base = it.get().value("noDelimiter").replaceAll("\\s", "");
    
    if (base.length() == 0) {
        false;
    } else {
        delimiter = base[0];
        if (delimiter == '\\\\') {
            false;
        } else {
            true;
        }
    }
}
GREMLIN;

    public const MAKE_DELIMITER_FINAL = <<<'GREMLIN'
sideEffect{ 
         if (delimiter == "{") { delimiter = "\\{";   delimiterFinal = "\\}"; } 
    else if (delimiter == "}") { delimiter = "\\}";   delimiterFinal = "\\}"; } 
    else if (delimiter == "(") { delimiter = "\\(";   delimiterFinal = "\\)"; } 
    else if (delimiter == ")") { delimiter = "\\)";   delimiterFinal = "\\)"; } 
    else if (delimiter == "[") { delimiter = "\\[";   delimiterFinal = "\\]"; } 
    else if (delimiter == "]") { delimiter = "\\]";   delimiterFinal = "\\]"; } 
    else if (delimiter == "*") { delimiter = "\\*";   delimiterFinal = "\\*"; } 
    else if (delimiter == "|") { delimiter = "\\|";   delimiterFinal = "\\|"; } 
    else if (delimiter == "?") { delimiter = "\\?";   delimiterFinal = "\\?"; } 
    else if (delimiter == "+") { delimiter = "\\+";   delimiterFinal = "\\+"; } 
    else if (delimiter == '$') { delimiter = "\\\$";  delimiterFinal = "\\\$"; } 
    else if (delimiter == ".") { delimiter = "\\.";   delimiterFinal = "\\." ; } 
    
    // default case : accept
    else { delimiterFinal = delimiter; } 
}
// Remove any invalid delimiter
.filter{ !(delimiter in ["\\", 
                         "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
                         "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
                         "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"
                         ]); }

GREMLIN;

    public function analyze(): void {
        $functions = '\\preg_replace';

        // preg_match with a string
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 0)
             ->tokenIs('T_CONSTANT_ENCAPSED_STRING')
             ->isNot('noDelimiter', '')
             ->raw(self::FETCH_DELIMITER)
             ->raw(self::MAKE_DELIMITER_FINAL)
             ->regexIs('noDelimiter', '^\\\\s*(" + delimiter + ").*(" + delimiterFinal + ")([a-df-zA-Z]*?e[a-df-zA-Z]*?)\$')
             ->back('first');
        $this->prepareQuery();

        // With an interpolated string "a $x b"
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->outWithRank('CONCAT', 0)
             ->isNot('noDelimiter', '')
             ->raw(self::FETCH_DELIMITER)
             ->inIs('CONCAT')
             ->raw(self::MAKE_DELIMITER_FINAL)
             ->regexIs('fullcode', '^.\\\\s*(" + delimiter + ").*(" + delimiterFinal + ")([a-df-zA-Z]*?e[a-df-zA-Z]*?).\$')
             ->back('first');
        $this->prepareQuery();

        // with a concatenation
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Concatenation')
             ->outWithRank('CONCAT', 0)
             ->atomIs('String')
             ->outIsIE('CONCAT')
             ->atomIs('String')
             ->is('rank', 0)
             ->isNot('noDelimiter', '')
             ->raw(self::FETCH_DELIMITER)
             ->inIsIE('CONCAT')
             ->raw(self::MAKE_DELIMITER_FINAL)
             ->regexIs('fullcode', '^.\\\\s*(" + delimiter + ").*(" + delimiterFinal + ")([a-df-zA-Z]*?e[a-df-zA-Z]*?).\$')
             ->back('first');
        $this->prepareQuery();
// Actual letters used for Options in PHP imsxeuADSUXJ (others may yield an error) case is important

        $this->atomFunctionIs(array('\\mb_eregi_replace',
                                    '\\mb_ereg_replace',
                                    ))
             ->outWithRank('ARGUMENT', 3)
             ->atomIs('String')
             ->tokenIs('T_CONSTANT_ENCAPSED_STRING')
             ->regexIs('noDelimiter', 'e')
             ->back('first');
         $this->prepareQuery();
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Common\WithoutTry;

class EvalWithoutTry extends WithoutTry {
    protected $phpVersion = '7.0+';

    public function analyze(): void {
        $this->atoms = array('Eval');
        parent::analyze();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UseInstanceof extends Analyzer {
    public function analyze(): void {
        // get_class($x) == 'Function'
        $this->atomIs('Comparison')
             ->analyzerIsNot('self')
             ->outIs('LEFT')
             ->functioncallIs('\\get_class')
             ->back('first');
        $this->prepareQuery();

        // 'Function' == get_class($x)
        $this->atomIs('Comparison')
             ->analyzerIsNot('self')
             ->outIs('RIGHT')
             ->functioncallIs('\\get_class')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Type;

use Exakat\Analyzer\Analyzer;

class SilentlyCastInteger extends Analyzer {
    public function analyze(): void {
        // Binary or hexadecimal, cast to Float
        $this->atomIs('Float')
             ->regexIs('fullcode', '^0[xXbB]');
        $this->prepareQuery();

        // Too long integer
        $this->atomIs('Float')
             ->regexIs('fullcode', '^[0-9]+\\$')
             ->regexIsNot('fullcode', '\\\\.');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class TimestampDifference extends Analyzer {
    public function analyze(): void {
        // All kinds of addition are allowed here :
        // microtime() - 1 or microtime() + -3

        // microtime(true) - microtime(true)
        $this->atomIs('Addition')
             ->outIs(array('LEFT', 'RIGHT'))
             ->functioncallIs(array('\\time', '\\microtime'))
             ->back('first');
        $this->prepareQuery();

        // date('U') - $d
        $this->atomIs('Addition')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Functioncall', self::WITH_VARIABLES)
             ->fullnspathIs('\\date')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->noDelimiterIs('U')
             ->back('first');
        $this->prepareQuery();

        // dateTime->format('U') - $d
        $this->atomIs('Addition')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Methodcall', self::WITH_VARIABLES)
             ->as('method')
             ->outIs('METHOD')
             ->codeIs('format', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->noDelimiterIs('U')

             // No check on class yet
             // it should accepte datetime and datetimeimmutable

             ->back('first');
        $this->prepareQuery();

        // Note : date('U') is the exact call to date.

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class RedefinedConstants extends Analyzer {
    public function analyze(): void {
        $this->atomIs('Class')
             ->outIs('CONST')
             ->atomIs('Const')
             ->outIs('CONST')
             ->as('results')
             ->outIs('NAME')
             ->savePropertyAs('code', 'constante')
             ->back('first')
             ->goToAllParents(self::EXCLUDE_SELF)
             ->outIs('CONST')
             ->atomIs('Const')
             ->outIs('CONST')
             ->outIs('NAME')
             ->samePropertyAs('code', 'constante', self::CASE_SENSITIVE)
             ->back('results');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class RedefinedDefault extends Analyzer {
    public function analyze(): void {
        // class x { private $y = 1; function __construct() { $this->y = 2;}}
        $this->atomIs('Ppp')
             ->outIs('PPP')
             ->atomIs('Propertydefinition')
             ->savePropertyAs('propertyname', 'name')
             ->filter(
                $this->side()
                     ->outIs('DEFAULT')
                     ->hasNoIn('RIGHT')
                     ->atomIsNot('Void')
             )
             ->as('results')
             ->goToClass()

             ->outIs('MAGICMETHOD')
             ->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs('__construct')
             ->inIs('NAME')
             ->outIs('BLOCK')
             ->outIs('EXPRESSION')
// Not using atomInside, to avoid values in a condition
//             ->atomInside('Assignation')
             ->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->is('constant', true)
             ->inIsIE('CODE')
             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->atomIs('Member')
             ->outIs('OBJECT')
             ->atomIs('This')
             ->inIs('OBJECT')
             ->outIs('MEMBER')
             ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)

             // sameParameterAs
             ->back('results');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class FopenMode extends Analyzer {
    public function analyze(): void {
        // fopen('path/to/file', 'bbc')
        $this->atomFunctionIs('\\fopen')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs('String') // No checks on variable or properties.
             ->hasNoOut('CONCAT')
             ->noDelimiterIsNot(array('r', 'r+', 'w', 'w+', 'a', 'a+', 'x', 'x+', 'c', 'c+', 'e',  // Normal
                                      'rb', 'r+b', 'wb', 'w+b', 'ab', 'a+b', 'xb', 'x+b', 'cb', 'c+b', 'eb',  // binary post
                                      'br', 'br+', 'bw', 'bw+', 'ba', 'ba+', 'bx', 'bx+', 'bc', 'bc+', 'be',  // binary pre
                                      'rt', 'r+t', 'wt', 'w+t', 'at', 'a+t', 'xt', 'x+t', 'ct', 'c+t', 'et',  // text post
                                      'tr', 'tr+', 'tw', 'tw+', 'ta', 'ta+', 'tx', 'tx+', 'tc', 'tc+', 'te',  // text pre
                                      ))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NegativePow extends Analyzer {
    public function analyze(): void {
        // -3 * 2 == -(3 ** 2) == 9 (not -9)
        $this->atomIs('Sign')
             ->codeIs('-')
             ->outIs('SIGN')
             ->atomIs('Power')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class BetterRand extends Analyzer {
    public function analyze(): void {
        // rand => mt_rand => random_int
        $this->atomFunctionIs(array('\rand',
                                    '\mt_rand',
                                    '\openssl_random_pseudo_bytes',
                                    '\uniqid',
                                    '\lcg_value',
                                    ));
        $this->prepareQuery();

        // sha1(microtime())
        $this->atomFunctionIs(array('\microtime',
                                    '\time',
                                    ))
             ->inIs('ARGUMENT')
             ->functioncallIs(array('\md5',
                                    '\sha1',
                                    '\sha2156',
                                    '\hash',
                                    '\crc32',
                                    '\crypt',
                                    '\str_rot13',
                                    '\strrev',
                                    '\uniqid',
                                    '\base64_encode',
                                    ))
            ->analyzerIsNot('self');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class TernaryInConcat extends Analyzer {
    public function analyze(): void {
        // 'a'. 'b'.$c > 1 ? 'd' : 'e'; Ternary has priority
        $this->atomIs(array('Ternary', 'Coalesce'))
             ->outIs(array('CONDITION', 'LEFT'))
             //->atomIs('Comparison')// Skip
             ->outIsIE('LEFT')
             ->atomIs('Concatenation')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class IdenticalConditions extends Analyzer {
    public function analyze(): void {

        // $a || $a
        // ($a) && ($a)
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->hasNoIn(array('LEFT', 'RIGHT'))
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIsNot(self::LOGICAL_ALL)
             ->savePropertyAs('fullcode', 'left')
             ->inIsIE('CODE')
             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIsNot(self::LOGICAL_ALL)
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $a || $a || $a
        // ($a) && ($a)
        // two levels
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'right')
             ->inIsIE('CODE')
             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->samePropertyAs('fullcode', 'right', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'left')
             ->inIsIE('CODE')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // case for $a || $b || $b
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             // Ignore LEFT
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)

             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'right')
             ->inIsIE('CODE')
             ->inIs('RIGHT')

             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->samePropertyAs('fullcode', 'right', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $a || $a || $a
        // ($a) && ($a)
        // three levels
        // straight structure
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'left')
             ->inIsIE('CODE')
             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // reverse structure
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'left')
             ->inIsIE('CODE')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $a || $a || $a
        // ($a) && ($a)
        // four levels
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'left')
             ->inIsIE('CODE')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $a || $a || $a
        // ($a) && ($a)
        // four levels
        $this->atomIs(self::LOGICAL_ALL)
             ->analyzerIsNot('self')
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->savePropertyAs('fullcode', 'left')
             ->inIsIE('CODE')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->outIsIE('CODE')
             ->atomIs(self::LOGICAL_ALL)
             ->outIs(array('RIGHT', 'LEFT'))
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // TODO : also adding situations like ($a and !$a) ?
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoChoice extends Analyzer {
    public function analyze(): void {
        $skipExpression = array('Closure',
                                'Arrowfunction',
                                'Switch',
                                'Match',
                                'For',
                                'Foreach',
                                'While',
                                'Dowhile',
                                'Yield',
                                'Yieldfrom',
                                'Ifthen',
                                );

        // $a == 2 ? doThis() : doThis();
        $this->atomIs('Ternary')
             ->outIs('THEN')
             ->savePropertyAs('fullcode', 'cdt')
             ->inIs('THEN')
             ->outIs('ELSE')
             ->samePropertyAs('fullcode', 'cdt', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $a == 2 ?: doThis();
        $this->atomIs('Ternary')
             ->filter(
                $this->side()
                     ->outIs('THEN')
                     ->atomIs('Void')
                     ->count()
                     ->isEqual(1)
              )
             ->outIs('CONDITION')
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'cdt')
             ->inIs('CONDITION')
             ->outIs('ELSE')
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'cdt', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // if ($a == 2) Then doThis(); else doThis();
        $this->atomIs('Ifthen')
             ->outIs('THEN')
             ->atomIs('Sequence')

             // exclude large structures, as fullcode is not sufficient
             ->not(
                $this->side()
                     ->outIs('EXPRESSION')
                     ->atomIs($skipExpression)
             )
             ->raw('sideEffect{ sthen = []; it.get().vertices(OUT, "EXPRESSION").sort{it.value("rank")}.each{ sthen.add(it.value("fullcode"));} }')
             ->inIs('THEN')
             ->outIs('ELSE')
             ->atomIs('Sequence')
             // exclude large structures, as fullcode is not sufficient
             ->not(
                $this->side()
                     ->outIs('EXPRESSION')
                     ->atomIs($skipExpression)
             )
             ->raw('sideEffect{ selse = []; it.get().vertices(OUT, "EXPRESSION").sort{it.value("rank")}.each{ selse.add(it.value("fullcode"));} }')
             ->raw('filter{ sthen.join(";") == selse.join(";") }')
             ->back('first');
        $this->prepareQuery();

        // $a == 2 ?: false;
        $this->atomIs('Ternary')
             ->filter(
                $this->side()
                     ->outIs('THEN')
                     ->atomIs('Void')
                     ->count()
                     ->isEqual(1)
              )
             ->outIs('ELSE')
             ->atomIs('Boolean', self::WITH_CONSTANTS)
             ->fullnspathIs('\\false')
             ->back('first');
        $this->prepareQuery();

        // $a == 2 ?? null;
        $this->atomIs('Coalesce')
             ->outIs('RIGHT')
             ->atomIs('Null', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();

        // doThis() ?? doThis();
        $this->atomIs('Coalesce')
             ->outIs('LEFT')
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'cdt')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'cdt', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class LogicalMistakes extends Analyzer {
    public function analyze(): void {
        // Note : support for parenthesis is added.

        //if ( $a != 1 || $a != 2)
        $this->atomIs('Logical')
             ->codeIs(array('||', 'or'))
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('!=', '!=='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'var')
             ->inIs(array('LEFT', 'RIGHT'))
             ->inIsIE('CODE')
             ->inIs('LEFT')

             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('!=', '!=='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'var')
             ->back('first');
        $this->prepareQuery();

        //if ( $a == 1 || $a != 2)
        $this->atomIs('Logical')
             ->codeIs(array('||', 'or'))
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('==', '==='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'var')
             ->inIs(array('LEFT', 'RIGHT'))
             ->inIsIE('CODE')
             ->inIs('LEFT')

             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('!=', '!=='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'var')
             ->back('first');
        $this->prepareQuery();

        //if ( $a == 1 && $a == 2)
        $this->atomIs('Logical')
             ->codeIs(array('&&', 'and'))
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('==', '==='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'var')
             ->inIs(array('LEFT', 'RIGHT'))

             // check that the second part is NOT another containers
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIsNot(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'literal')
             ->inIs(array('LEFT', 'RIGHT'))

             ->inIsIE('CODE')
             ->inIs('LEFT')

             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('==', '==='))

             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIsNot(self::CONTAINERS)
             ->notSamePropertyAs('fullcode', 'literal')
             ->inIs(array('LEFT', 'RIGHT'))

             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'var')


             ->back('first');
        $this->prepareQuery();

        //if ( $a == 1 && $a != 2)
        $this->atomIs('Logical')
             ->codeIs(array('&&', 'and'))
             ->outIs('LEFT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('==', '==='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'var')
             ->inIs(array('LEFT', 'RIGHT'))
             ->inIsIE('CODE')
             ->inIs('LEFT')

             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIs('Comparison')
             ->codeIs(array('!=', '!=='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'var')
             ->back('first');
        $this->prepareQuery();

        // Extension to this rule :
        // Check for methodcalls, function calls
        // add support for xor (although, it is rare)
        // may be invert == and != ?
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class SameConditions extends Analyzer {
    public function analyze(): void {
        // if ($a) {} elseif ($a) {} else {}
        // if ($a || $b) {} elseif ($a1) {} else {}
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->optional(
                $this->side()
                     ->atomIs('Logical')
                     ->codeIs(array('||', 'or'), self::TRANSLATE, self::CASE_INSENSITIVE)
                     ->goToAllRight()
             )
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->savePropertyAs('fullcode', 'condition')
             ->as('results')

             ->back('first')
             ->outIs(array('THEN', 'ELSE'))
             ->atomInsideNoDefinition('Ifthen')
             ->outIs('CONDITION')
             ->optional(
                $this->side()
                     ->atomIs('Logical')
                     ->codeIs(array('||', 'or'), self::TRANSLATE, self::CASE_INSENSITIVE)
                     ->goToAllRight()
             )
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->samePropertyAs('fullcode', 'condition', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ReturnTrueFalse extends Analyzer {
    public function analyze(): void {

        // If ($a == 2) { return true; } else { return false; }
        // If ($a == 2) { return false; } else { return true; }
        $this->atomIs('Ifthen')

             ->outIs('THEN')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Return')
             ->outIs('RETURN')
             ->atomIs(array('Boolean', 'Null', 'Integer'))
             ->savePropertyAs('boolean', 'a')
             ->inIs('RETURN')
             ->inIs('EXPRESSION')
             ->inIs('THEN')

             ->outIs('ELSE')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Return')
             ->outIs('RETURN')
             ->atomIs(array('Boolean', 'Null', 'Integer'))
             ->notSamePropertyAs('boolean', 'a')

             ->back('first');
        $this->prepareQuery();

        // If ($a == 2) { $b = true; } else { $b = false; }
        // If ($a == 2) { $b = false; } else { $b = true; }
        $this->atomIs('Ifthen')
             ->outIs('THEN')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Assignation')

             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'container')
             ->inIs('LEFT')

             ->outIs('RIGHT')
             ->atomIs(array('Boolean', 'Null', 'Integer'))
             ->savePropertyAs('boolean', 'valeur')
             ->back('first')

             ->outIs('ELSE')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Assignation')

             ->outIs('LEFT')
             ->samePropertyAs('fullcode', 'container')
             ->inIs('LEFT')

             ->outIs('RIGHT')
             ->atomIs(array('Boolean', 'Null', 'Integer'))
             ->notSamePropertyAs('boolean', 'valeur')

             ->back('first');
        $this->prepareQuery();

        // $a = ($b == 2) ? true : false;
        // $a = ($b == 2) ? false : true;
        $this->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs('Ternary')

             ->outIs('THEN')
             ->atomIs('Boolean')
             ->savePropertyAs('boolean', 'a')
             ->inIs('THEN')

             ->outIs('ELSE')
             ->atomIs('Boolean')
             ->notSamePropertyAs('boolean', 'a')

             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class CouldUseDir extends Analyzer {
    public function analyze(): void {
        $this->atomFunctionIs('\\dirname')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Magicconstant')
             ->codeIs('__FILE__')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class ShouldUseCoalesce extends Analyzer {
    protected $phpVersion = '7.0+';

    public function analyze(): void {
        //isset($a) ? $a : 'b';
        $this->atomIs('Ternary')
             ->outIs('CONDITION')
             ->atomIs('Isset')
             ->outWithRank('ARGUMENT', 0)
             ->savePropertyAs('fullcode', 'variable')
             ->inIs('ARGUMENT')
             ->inIs('CONDITION')
             ->outIs('THEN')
             ->samePropertyAs('fullcode', 'variable')
             ->back('first');
        $this->prepareQuery();

        //!isset($a) ? 'b' : $a;
        $this->atomIs('Ternary')
             ->outIs('CONDITION')
             ->atomIs('Not')
             ->outIs('NOT')
             ->atomIs('Isset')
             ->outWithRank('ARGUMENT', 0)
             ->savePropertyAs('fullcode', 'variable')
             ->inIs('ARGUMENT')
             ->inIs('NOT')
             ->inIs('CONDITION')
             ->outIs('ELSE')
             ->samePropertyAs('fullcode', 'variable')
             ->back('first');
        $this->prepareQuery();

        //$a === null ? $a : 'b';
        $this->atomIs('Ternary')
             ->outIs('CONDITION')
             ->atomIs('Comparison')
             ->codeIs('===')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Null')
             ->inIs(array('LEFT', 'RIGHT'))
             ->outIs(array('LEFT', 'RIGHT')) // Out to the other one, in fact
             ->savePropertyAs('fullcode', 'variable')
             ->inIs(array('LEFT', 'RIGHT'))
             ->inIs('CONDITION')
             ->outIs('THEN')
             ->samePropertyAs('fullcode', 'variable')
             ->back('first');
        $this->prepareQuery();

        //$a !== null ?  'b' : $a;
        $this->atomIs('Ternary')
             ->outIs('CONDITION')
             ->atomIs('Comparison')
             ->codeIs('!==')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Null')
             ->inIs(array('LEFT', 'RIGHT'))
             ->outIs(array('LEFT', 'RIGHT'))
             ->tokenIsNot('T_STRING')
             ->savePropertyAs('fullcode', 'variable')
             ->inIs(array('LEFT', 'RIGHT'))
             ->inIs('CONDITION')
             ->outIs('ELSE')
             ->samePropertyAs('fullcode', 'variable')
             ->back('first');
        $this->prepareQuery();

        //if (($model = Model::get($id)) === NULL) { $model = $default_model; }
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->atomIs('Comparison')
             ->codeIs('===')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Null')
             ->inIs(array('LEFT', 'RIGHT'))
             ->outIs(array('LEFT', 'RIGHT'))
             ->outIsIE('CODE')
             ->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'variable')
             ->inIs('LEFT')
             ->inIsIE('CODE')
             ->inIs(array('LEFT', 'RIGHT'))
             ->inIs('CONDITION')
             ->outIs('THEN')
             ->outWithRank('EXPRESSION', 0)
             ->outIs('LEFT')
             ->samePropertyAs('fullcode', 'variable')
             ->back('first');
        $this->prepareQuery();

        //isset($a) ?: $b;
        $this->atomIs('Ternary')
             ->outIs('THEN')
             ->atomIs('Void')
             ->back('first')
             ->outIs('CONDITION')
             ->atomIs('Isset')
             ->back('first');
        $this->prepareQuery();

        //is_null($a) ?? $b;
        $this->atomIs('Coalesce')
             ->outIs('LEFT')
             ->functioncallIs('\\is_null')
             ->back('first');
        $this->prepareQuery();

        //isset($a) ?? $b;
        $this->atomIs('Coalesce')
             ->outIs('LEFT')
             ->atomIs('Isset')
             ->back('first');
        $this->prepareQuery();

        //$a == null ?? $b;
        $this->atomIs('Coalesce')
             ->outIs('LEFT')
             ->atomIs('Comparison')
             ->codeIs(array('==', '===', '!=', '==='))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Null')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class IfWithSameConditions extends Analyzer {
    public function analyze(): void {
        // if ($a) {}
        // if ($a) {} // repeated
        $this->atomIs('Ifthen')
             ->outIs('CONDITION')
             ->savePropertyAs('fullcode', 'condition')
             ->inIs('CONDITION')
             ->nextSibling()
             ->outIs('CONDITION')
             ->samePropertyAs('fullcode', 'condition')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Exceptions;

use Exakat\Analyzer\Analyzer;

class ThrowFunctioncall extends Analyzer {
    public function analyze(): void {
        $phpClasses    = $this->loadIni('php_classes.ini', 'classes');
        $phpClassesFnp = makeFullNsPath($phpClasses);

        // throw className(), defined class, no function
        $this->atomIs('Throw')
             ->outIs('THROW')
             ->tokenIs(array_merge(self::STATICCALL_TOKEN, array('T_OBJECT_OPERATOR', 'T_DOUBLE_COLON', 'T_NULLSAFE_OBJECT_OPERATOR', 'T_OPEN_BRACKET')))
             ->atomIs(array('Functioncall', 'Staticmethodcall', 'Methodcall'))
             ->hasNoFunctionDefinition()
             ->back('first');
        $this->prepareQuery();

        // todo : consider typehint : a functioncall may return an exception, a property may be typed Exception.

        // throw RuntimeException()
        $this->atomIs('Throw')
             ->outIs('THROW')
             ->tokenIs(self::STATICCALL_TOKEN)
             ->atomIsNot(array('Array', 'Functioncall'))
             ->fullnspathIs($phpClassesFnp)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UseInstanceof extends Analyzer {
    public function analyze(): void {
        // is_object()
        $this->atomFunctionIs('\\is_object');
        $this->prepareQuery();

        // check for class_implements call too.
        // relax on !is_object (or suggest is_scalar)
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ResultMayBeMissing extends Analyzer {
    public function analyze(): void {
        $this->atomFunctionIs('\\preg_match')
             ->outWithRank('ARGUMENT', 2)
             ->savePropertyAs('code', 'results')
             ->back('first')
             ->hasIn('EXPRESSION')
             ->nextSibling()
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs('Array')
             ->outIsIE('VARIABLE')
             ->samePropertyAs('code', 'results')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NeverNegative extends Analyzer {
    public function analyze(): void {
        // count($a) >= 0 (always true)
        $this->atomIs('Comparison')
             ->codeIs(array('>=', '<'))
             ->outIs('RIGHT')
             ->codeIs('0')
             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->functioncallIs(array('\\count', '\\sizeof', '\\strlen', '\\abs'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class EmptyBlocks extends Analyzer {
    public function analyze(): void {
        // Block with only one empty expression
        // Block with only empty expressions
        $this->atomIs(array('For', 'While', 'Foreach', 'Dowhile', 'Declare', 'Namespace', 'Declare', 'Switch', 'Match'))
             ->not(
                $this->side()
                     ->outIs('DECLARE')
                     ->outIs('NAME')
                     ->codeIs('strict_types')
             )
             ->outIs(array('CASES', 'BLOCK'))
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outIs('EXPRESSION')
                             ->atomIsNot('Void')
                     )
             )
             ->back('first');
        $this->prepareQuery();

        // Empty block on ifthen
        $this->atomIs('Ifthen')
             ->outIs(array('THEN', 'ELSE'))
             ->atomIs('Sequence')
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outIs('EXPRESSION')
                             ->atomIsNot('Void')
                     )
             )
             ->back('first')
             ->inIsIE('ELSE');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class ThrowInDestruct extends Analyzer {
    public function analyze(): void {
        // class x { function __destruct() { throw new Exception(); }}
        $this->atomIs('Class')
             ->outIs('MAGICMETHOD')
             ->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs('__destruct')
             ->inIs('NAME')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Throw')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UseSystemTmp extends Analyzer {
    public function analyze(): void {
        $functions = array('\\glob',
                           '\\fopen',
                           '\\file',
                           '\\file_get_contents',
                           '\\file_put_contents',
                           '\\unlink',
                           '\\opendir',
                           '\\rmdir',
                           '\\mkdir',
                           );
        $regexStartWithTmp = '^(/tmp/|C:\\\\\\\\WINDOWS\\\\\\\\TEMP|C:\\\\\\\\WINDOWS)';

        // string literal fopen('a', 'r');
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->regexIs('noDelimiter', $regexStartWithTmp)
             ->back('first');
        $this->prepareQuery();

        // string literal fopen("a$b", 'r');
        // may need some regex to exclude http...
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->is('constant', true)
             ->outWithRank('CONCAT', 0)
             ->regexIs('noDelimiter', $regexStartWithTmp)
             ->back('first');
        $this->prepareQuery();

        // string literal fopen('a'.$b, 'r');
        // may need some regex to exclude http...
        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Concatenation')
             ->is('constant', true)
             ->outWithRank('CONCAT', 0)
             ->regexIs('noDelimiter', $regexStartWithTmp)
             ->back('first');
        $this->prepareQuery();
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Namespaces;

use Exakat\Analyzer\Analyzer;

class HiddenUse extends Analyzer {
    public function analyze(): void {
        // only for uses with rank of 1 or later
        $this->atomIs(array('Usenamespace', 'Usetrait'))
             ->savePropertyAs('rank', 'ranked')
             ->inIs('EXPRESSION')
             ->filter(
                $this->side()
                     ->outIs('EXPRESSION')
                     ->has('rank')
                     ->isLess('rank', 'ranked')
                     ->atomIsNot(array('Usenamespace', 'Usetrait', 'Declare', 'Include'))
               )
             ->back('first');
        $this->prepareQuery();

        // rank = 0 use are OK
        // inside a class/trait
        $this->atomIs(array('Usenamespace', 'Usetrait'))
             ->savePropertyAs('rank', 'ranked')
             ->inIs('USE')
             ->filter(
                $this->side()
                     ->outIs(array('CONST', 'METHOD', 'PPP'))
                     ->has('rank')
                     ->isLess('rank', 'ranked')
             )
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Namespaces;

use Exakat\Analyzer\Analyzer;

class ShouldMakeAlias extends Analyzer {
    public function analyze(): void {
        // No namespace ?
        $this->atomIs(array('Nsname', 'Newcall'))
             ->tokenIs(array('T_NS_SEPARATOR', 'T_NAME_RELATIVE', 'T_NAME_FULLY_QUALIFIED', 'T_NAME_QUALIFIED'))
             ->hasNoIn(array('USE', 'NAME'))
             ->hasNoParent('Usenamespace', array('NAME', 'USE'))  // use expression
             ->hasNoParent('Namespace', 'NAME')  // use expression
             ->has('fullnspath')
             ->savePropertyAs('fullnspath', 'possibleAlias')
             ->goToNamespace()
             ->raw(<<<'GREMLIN'
where( __.out("BLOCK", "CODE").out("EXPRESSION")
         .hasLabel("Usenamespace").out("USE")
         .filter{ (possibleAlias =~ "^" + it.get().value("fullnspath").replace("\\", "\\\\") ).getCount() > 0} )
GREMLIN
)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class MultipleTraitOrInterface extends Analyzer {
    public function analyze(): void {
        // interfaces
        $this->atomIs('Class')
             ->raw(<<<'GREMLIN'
where( __.sideEffect{counts = [:]}
         .out("IMPLEMENTS")
         .sideEffect{ k = it.get().value("fullnspath"); 
                    if (counts[k] == null) {
                       counts[k] = 1;
                    } else {
                       counts[k]++;
                    }
         }.fold()
       )
       .sideEffect{ names = counts.findAll{ a,b -> b > 1}.keySet() }
       .out("IMPLEMENTS")
       .filter{ it.get().value("fullnspath") in names }
GREMLIN
)
             ->back('first');
             $this->prepareQuery();

        // traits
             $this->atomIs('Class')
             ->raw(<<<'GREMLIN'
where( __.sideEffect{counts = [:]}
         .out("USE").hasLabel("Usetrait").out("USE")
         .sideEffect{ k = it.get().value("fullnspath"); 
                    if (counts[k] == null) {
                       counts[k] = 1;
                    } else {
                       counts[k]++;
                    }
         }.fold()
       )
       .sideEffect{ names = counts.findAll{ a,b -> b > 1}.keySet() }
       .out("USE").hasLabel("Usetrait").out("USE")
       .filter{ it.get().value("fullnspath") in names }
GREMLIN
)
             ->back('first');
             $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Namespaces;

use Exakat\Analyzer\Analyzer;

class MultipleAliasDefinitions extends Analyzer {
    public function analyze(): void {
        // alias with varied values
        $aliasQuery = <<<'GREMLIN'
g.V().hasLabel("Usenamespace").out("USE")
     .group("a").by("alias").by("fullnspath")
     .cap("a").next()
     .findAll{a,b -> b.unique().size() > 1}.keySet()
GREMLIN;
        $aliases = $this->query($aliasQuery)->toArray();

        if (empty($aliases)) {
            return ;
        }

        $this->atomIs('Usenamespace')
             ->outIs('USE')
             ->is('alias', $aliases)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class FailingSubstrComparison extends Analyzer {

    public function analyze(): void {
        // substr($a, 0, 3) === 'abcdef';
        $this->atomIs('Comparison')
             ->codeIs(array('==', '==='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->outIs(array('LEFT', 'RIGHT'))
             ->functioncallIs('\substr')
             ->outWithRank('ARGUMENT', 2)
             ->atomIs('Integer', self::WITH_CONSTANTS)
             ->isMore('intval', 0)
             ->savePropertyAs('intval', 'length')
             ->back('first')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->has('noDelimiter')
             ->getStringLength('noDelimiter', 's')
             // Substring is actually as long as length
             ->raw('filter{ s != length.toInteger().abs(); }')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ShouldMakeTernary extends Analyzer {
    public function analyze(): void {
        // if ($a) $b = 2; else $b = 3;
        $this->atomIs('Ifthen')
             ->outIs('THEN')
             ->is('count', 1)
             ->outWithRank('EXPRESSION', 0)
             ->atomIs('Assignation')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'a')
             ->back('first')
             ->outIs('ELSE')
             ->is('count', 1)
             ->outWithRank('EXPRESSION', 0)
             ->atomIs('Assignation')
             ->outIs('LEFT')
             ->samePropertyAs('fullcode', 'a')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class DropElseAfterReturn extends Analyzer {
    public function analyze(): void {
        //if ($a) { return $a; } else { doSomething(); }
        $this->atomIs('Ifthen')
             ->tokenIsNot('T_ELSEIF')
             ->raw(<<<'GREMLIN'
not(
    where(
        __.in("EXPRESSION")
          .has("count", 1)
          .in("ELSE")
          .hasLabel("Ifthen")
    )
)
GREMLIN
)
             ->outIs('THEN')
             ->outIs('EXPRESSION')
             ->atomIs('Return')
             ->back('first')
             ->outIs('ELSE')
             ->hasNoChildren('Return', array('EXPRESSION'))
             ->back('first');
        $this->prepareQuery();

        //if ($a) { doSomething(); } else { return $a; }
        $this->atomIs('Ifthen')
             ->tokenIsNot('T_ELSEIF')
             ->raw(<<<'GREMLIN'
not(
    where(
        __.in("EXPRESSION")
          .has("count", 1)
          .in("ELSE")
          .hasLabel("Ifthen")
    )
)
GREMLIN
)
             ->outIs('ELSE')
             ->outIs('EXPRESSION')
             ->atomIs('Return')
             ->back('first')
             ->outIs('THEN')
             ->hasNoChildren('Return', array('EXPRESSION'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UseClassOperator extends Analyzer {
    public function analyze(): void {
        // $a = '\x'; class x {}
        $this->atomIs(self::STRINGS_LITERALS) // first one for optimization purposes
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->has('noDelimiter')
             ->noDelimiterIsNot(array('static', 'self', 'parent'))
             ->regexIsNot('fullcode', '::')
             ->inIs('DEFINITION')
             ->atomIs('Class')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Security;

use Exakat\Analyzer\Analyzer;

class DontEchoError extends Analyzer {
    public function analyze(): void {
        // echo mysql_error();
        $errorMessageFunctions = $this->loadIni('errorMessageFunctions.ini', 'functions');
        $errorMessageFunctions = makeFullNsPath($errorMessageFunctions);

        $displayFunctions = $this->loadIni('displayFunctions.ini', 'functions');
        $displayFunctions = makeFullNsPath($displayFunctions);

        $this->atomFunctionIs($displayFunctions)
             ->outIs('ARGUMENT')
             ->atomIs('Functioncall')
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outIs('NAME')
                             ->atomIs(array('Array', 'Variable', 'Member', 'Staticproperty', 'Methodcall', 'Staticmethodcall'))
                     )
             )
             ->has('fullnspath')
             ->fullnspathIs($errorMessageFunctions)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(array('Echo', 'Print', 'Exit'))
             ->outIs('ARGUMENT')
             ->outIsIE('CODE')
             ->atomIs('Functioncall')
             ->has('fullnspath')
             ->fullnspathIs($errorMessageFunctions)
             ->back('first');
        $this->prepareQuery();

        // echo 'error '.pg_error();
        $this->atomFunctionIs($displayFunctions)
             ->outIs('ARGUMENT')
             ->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->atomIs('Functioncall')
             ->fullnspathIs($errorMessageFunctions)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(array('Echo', 'Print', 'Exit'))
             ->outIs('ARGUMENT')
             ->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->atomIs('Functioncall')
             ->fullnspathIs($errorMessageFunctions)
             ->back('first');
        $this->prepareQuery();

        // try {} catch ($e) { echo $e->getMessage(); }
        $this->atomIs('Try')
             ->outIs('CATCH')
             ->outIs('VARIABLE')
             ->savePropertyAs('code', 'exception')
             ->inIs('VARIABLE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Methodcall')
             ->outIs('OBJECT')
             ->samePropertyAs('code', 'exception')
             ->inIs('OBJECT')
             ->outIs('METHOD')
             ->codeIs(array('getMessage', 'getTraceAsString'), self::TRANSLATE, self::CASE_INSENSITIVE)
             ->inIs('METHOD')
             ->inIs('ARGUMENT')
             ->atomIs(array('Echo', 'Print', 'Exit', 'Functioncall'))
             ->has('fullnspath')
             ->fullnspathIs($displayFunctions);
        $this->prepareQuery();

        // try {} catch ($e) { echo $e.PHP_EOL; }
        $this->atomIs('Try')
             ->outIs('CATCH')
             ->outIs('VARIABLE')
             ->savePropertyAs('code', 'exception')
             ->inIs('VARIABLE')
             ->outIs('BLOCK')
             ->atomInsideNoDefinition(array('Echo', 'Print', 'Exit', 'Functioncall'))
             ->has('fullnspath')
             ->as('results')
             ->atomIs(array('Echo', 'Print', 'Exit', 'Functioncall'))
             ->has('fullnspath')
             ->fullnspathIs($displayFunctions)
             ->outIs('ARGUMENT')
             ->outIsIE('CONCAT')
             ->atomIs('Variable')
             ->samePropertyAs('code', 'exception')
             ->back('results');
        $this->prepareQuery();

        // ini_set('display_error', 1)
        $this->atomFunctionIs('\\ini_set')
             ->outWithRank('ARGUMENT', 0)
             ->has('noDelimiter')
             ->noDelimiterIs('display_errors')
             ->back('first')
             ->outWithRank('ARGUMENT', 1)
             ->has('boolean')
             ->is('boolean', true)
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoIssetWithEmpty extends Analyzer {
    public function analyze(): void {
        $this->atomIs('Logical')
             ->tokenIs('T_BOOLEAN_AND')
             ->outIs('LEFT')
             ->atomIs('Isset')
             ->outIs('ARGUMENT')
             ->savePropertyAs('fullcode', 'variable')
             ->back('first')
             ->outIs('RIGHT')
             ->outIsIE('NOT')            // optional !
             ->atomIs('Empty')
             ->outIs('ARGUMENT')
             ->samePropertyAs('fullcode', 'variable')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class UselessCheck extends Analyzer {
    public function analyze(): void {
        // No check on empty() and isset(), as they also check the variable existence
        //    if (count($anArray) > 0){    foreach ($anArray as $el){
        $this->atomIs('Ifthen')
             ->hasNoOut('ELSE')
             ->outIs('CONDITION')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->outIsIE(array('LEFT', 'RIGHT'))
             // count($a) > 0, sizeof($a) != 0
             ->functioncallIs(array('\\count', '\\sizeof'))
             ->outWithRank('ARGUMENT', 0)
             ->savePropertyAs('fullcode', 'var')
             ->not(
                $this->side()
                     ->inIs('ARGUMENT')
                     ->inIs(array('LEFT', 'RIGHT'))
                     ->atomIs('Comparison')
                     ->outIs(array('LEFT', 'RIGHT'))
                     ->atomIs('Integer')
                     ->is('fullcode', 0)
             )
             ->back('first')

             ->outIs('THEN')
             ->is('count', 1)
             ->outWithRank('EXPRESSION', 0)
             ->atomIs('Foreach')
             ->outIs('SOURCE')
             ->samePropertyAs('fullcode', 'var')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Namespaces;

use Exakat\Analyzer\Analyzer;

class MultipleAliasDefinitionPerFile extends Analyzer {
    public function analyze(): void {
        // use A as B;
        // use A as C;
        $this->atomIs('Usenamespace')
             ->outIs('USE')
             ->as('results')
             ->savePropertyAs('fullnspath', 'usepath')
             ->savePropertyAs('fullcode', 'thecode')
             ->inIs('USE')
             ->inIs('EXPRESSION')
             ->outIs('EXPRESSION')
             ->atomIs('Usenamespace')
             ->outIs('USE')
             ->samePropertyAs('fullnspath', 'usepath')
             ->notSamePropertyAs('fullcode', 'thecode')
             ->back('results');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class DirThenSlash extends Analyzer {
    public function analyze(): void {
        // $a = __DIR__.'asb' : __DIR__ must be followed by /
        $this->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->atomIs('Magicconstant')
             ->is('fullcode', '__DIR__')
             ->nextSibling('CONCAT')
             ->atomIs('String', self::WITH_CONSTANTS)
             ->hasNoOut('CONCAT')
             ->regexIs('noDelimiter', '^[^/]')
             ->back('first');
        $this->prepareQuery();

        // $a = __DIR__."as$b" : __DIR__ must be followed by /
        $this->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->atomIs('Magicconstant')
             ->is('fullcode', '__DIR__')
             ->nextSibling('CONCAT')
             ->atomIs('String')
             ->hasOut('CONCAT')
             ->outWithRank('CONCAT', 0)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->regexIs('noDelimiter', '^[^/]')
             ->back('first');
        $this->prepareQuery();

        // $a = __DIR__.'asb' : __DIR__ must be followed by /
        $this->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->functioncallIs('\\dirname')
             ->nextSibling('CONCAT')
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->regexIs('noDelimiter', '^[^/]')
             ->back('first');
        $this->prepareQuery();

        // $a = __DIR__."as$b" : __DIR__ must be followed by /
        $this->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->functioncallIs('\\dirname')
             ->nextSibling('CONCAT')
             ->atomIs('String')
             ->hasOut('CONCAT')
             ->outWithRank('CONCAT', 0)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->regexIs('noDelimiter', '^[^/]')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class RepeatedRegex extends Analyzer {
    public function analyze(): void {
        $functionsList = array('\preg_match',
                               '\preg_filter',
                               '\preg_grep',
                               '\preg_replace',
                               '\preg_match_all',
                               '\preg_split',
                               '\preg_replace_callback_array',
                               '\preg_replace_callback',
                              );

        $this->atomFunctionIs($functionsList)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->raw(<<<'GREMLIN'
groupCount("m").by("code").cap("m").next().findAll{ a,b -> b > 1}.keySet()
GREMLIN
);
        $repeatedRegex = $this->rawQuery()->toArray();

        if (empty($repeatedRegex)) {
            return;
        }

        // regex
        $this->atomFunctionIs($functionsList)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->codeIs($repeatedRegex, self::NO_TRANSLATE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class NoClassInGlobal extends Analyzer {
    public function analyze(): void {
        $this->atomIs(array('Class', 'Trait', 'Interface', 'Function'))
             ->regexIs('fullnspath', '^\\\\\\\\[^\\\\\\\\]+\$');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class CouldUseStrrepeat extends Analyzer {
    public function analyze(): void {
        // for() { $a .= A; }
        $this->atomIs('Assignation')
             ->tokenIs('T_CONCAT_EQUAL')
             ->outIs('RIGHT')
             ->atomIs(array('String', 'Heredoc', 'Concatenation', 'Identifier', 'Nsname'))
             ->is('constant', true)
             ->inIs('RIGHT')
             ->is('rank', 0)
             ->inIs('EXPRESSION')
             ->is('count', 1)
             ->inIs('BLOCK')
             ->atomIs(array('For', 'Foreach'))
             ->not(
                $this->side()
                     ->outIs('VALUE')
                     ->is('reference', true)
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Type;

use Exakat\Analyzer\Analyzer;

class StringWithStrangeSpace extends Analyzer {
    public function analyze(): void {
        // $a = ' bac'; // space is actually a vertical tab
        $weirdSpaces = $this->load('weirdSpaces', 'space');

        $regex = '(' . implode('|', array_keys($weirdSpaces)) . '})';

        $this->atomIs(self::STRINGS_ALL)
             ->hasNoOut('CONCAT')
             ->regexIs('noDelimiter', $regex);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoEmptyRegex extends Analyzer {
    public static $pregFunctions = array('\\preg_match_all',
                                         '\\preg_match',
                                         '\\preg_replace',
                                         '\\preg_replace_callback',
                                         '\\preg_relace_callback_array',
                                         );

    public function analyze(): void {
        // preg_match(''.$b, $d, $d); Empty delimiter
        $this->atomFunctionIs(self::$pregFunctions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_ALL, self::WITH_CONSTANTS)
             ->outIsIE('CONCAT')
             ->tokenIs(array('T_CONSTANT_ENCAPSED_STRING', 'T_ENCAPSED_AND_WHITESPACE'))
             ->noDelimiterIs('')
             ->back('first');
        $this->prepareQuery();

        // preg_match('a'.$b, $d, $d); Non-alpha numerical delimiter
        $this->atomFunctionIs(self::$pregFunctions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_ALL, self::WITH_CONSTANTS)
             ->outWithRank('CONCAT', 0)
             ->outIsIE('CONCAT') // keep going in case
             ->is('rank', 0)
             ->tokenIs(array('T_CONSTANT_ENCAPSED_STRING', 'T_ENCAPSED_AND_WHITESPACE'))
             ->noDelimiterIsNot('')
             ->regexIs('noDelimiter', '^[A-Za-z0-9]')
             ->back('first');
        $this->prepareQuery();

        // preg_match('abc', $d, $d); Non-alpha numerical delimiter
        $this->atomFunctionIs(self::$pregFunctions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_ALL, self::WITH_CONSTANTS)
             ->hasNoOut('CONCAT')
             ->noDelimiterIsNot('')
             ->regexIs('noDelimiter', '^[A-Za-z0-9]')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NoReferenceOnLeft extends Analyzer {
    public function analyze(): void {
        // $a = &$b +  $c;
        $this->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('RIGHT')
             ->atomIs(array('Addition', 'Multiplication', 'Bitshift', 'Power', 'Concatenation', 'Instanceof', 'Logical', 'Comparison'))
             ->outIs('LEFT')
             ->is('reference', true)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassRemoteDefinitionWithGlobal extends Complete {
    public function analyze(): void {
        // $global->method()
        // global $global
        // $global = new Class()
        // class class { function method() {} }
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->as('method')
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')
              ->atomIs(array('Globaldefinition', 'Variabledefinition'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->atomIs('Virtualglobal', self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'method');
        $this->prepareQuery();

        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')
              ->atomIs(array('Globaldefinition', 'Variabledefinition'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->atomIs('Virtualglobal', self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassRemoteDefinitionWithInjection extends Complete {
    public function analyze(): void {
        $this->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->outIs('DEFINITION')
              ->inIs('TYPEHINT')
              ->outIs('NAME')
              ->outIs('DEFINITION')
              ->atomIs('Variable', self::WITHOUT_CONSTANTS)
              ->inIs('RIGHT')
              ->atomIs('Assignation', self::WITHOUT_CONSTANTS)
              ->outIs('LEFT')
              ->atomIs('Member', self::WITHOUT_CONSTANTS)

              ->inIs('DEFINITION')
              ->atomIs('Propertydefinition',  self::WITHOUT_CONSTANTS)
              ->outIs('DEFINITION')
              ->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->addEFrom('DEFINITION', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassRemoteDefinitionWithParenthesis extends Complete {
    public function analyze(): void {
        // (new x)->foo()
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->as('method')
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->outIs('OBJECT')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'method');
        $this->prepareQuery();

        // (new x)::foo()
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('OBJECT')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();

        // (new x)::foo()
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
              ->as('method')
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'method');
        $this->prepareQuery();

        // (new x)::foo()
        $this->atomIs('Staticproperty', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();

        // (new x)::FOO
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->as('constant')
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'constant');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class IsZero extends Analyzer {
    public function analyze(): void {
        // $a = $c - $c;
        // $a = $c + $d - $c;
        // $a = $c + $d -$e - $c;
        // $a = $d + $c -$e - $c;
        $minus = $this->dictCode->translate(array('-'));

        if (empty($minus)) {
            return;
        }

        $MAX_LOOPING = self::MAX_LOOPING;
        $labels = array('Variable',
                        'Integer',
                        'Member',
                        'Float',
                        'Staticproperty',
                        'Array',
                        'Functioncall',
                        'Staticmethodcall',
                        'Methodcall',
                        );
        $labelsList = makeList($labels);

        $this->atomIs('Addition')
             ->hasNoParent('Addition', array('LEFT'))
             ->hasNoParent('Addition', array('RIGHT'))
             ->raw(<<<GREMLIN
sideEffect{x = [:]; id2 = it.get().id();}
.where(
   __.sideEffect{ previous = 1; supervious = 1;}
     .repeat(
       __.where( __.sideEffect{ if (it.get().value("code") == $minus[0]) { p = -1; } else { p = 1;}}.out("LEFT", "RIGHT").hasLabel("Addition").sideEffect{ previous = p;}.fold())
         .where( __.sideEffect{ if (it.get().value("code") == $minus[0]) { p = -1; } else { p = 1;}}.out("SIGN").sideEffect{ previous = p;}.fold())
         .where( __.sideEffect{ if (it.get().value("code") == $minus[0]) { p = -1; } else { p = 1;}}.out("LEFT", "RIGHT", "CODE", "SIGN").hasLabel("Assignation", "Parenthesis", "Sign").sideEffect{ previous = 1; supervious *= p;}.fold())
         .not(hasLabel("Multiplication", "Power"))
         .out("LEFT", "RIGHT", "CODE", "SIGN")
   )
    .emit().times($MAX_LOOPING)
    .hasLabel($labelsList).not(where( __.in("LEFT").hasLabel("Assignation")))
    .sideEffect{ v = it.get().value("fullcode");}

    .where(__.in("RIGHT").sideEffect{ if (it.get().value("token") == 'T_MINUS') { inc = -1; } else { inc = 1;} }.fold())
    .where(__.in("LEFT").not(where(__.in("RIGHT"))).sideEffect{ inc = 1;}.fold())
    .where(__.in("LEFT").in("RIGHT").sideEffect{ if (it.get().value("token") == 'T_MINUS') { inc = -1; } else { inc = 1;} }.fold())
    .where(__.in("SIGN").sideEffect{ inc = previous * supervious;}.fold())
    .where(__.in("CODE").sideEffect{ inc = supervious;}.fold())

    .sideEffect{ 
        if ((v =~ "-" ).getCount() != 0 ) {
            inc *=  -1;
        }
        
        v = v.replaceAll('\\\+', '').replaceAll('\\\-', ''); 
        
        if (x[v] == null) {
           x[v] = 0;
        }
        
        x[v] += inc; 
    
    }
    .fold()
)
.filter{ x.findAll{a,b -> b == 0} != [:]; }

GREMLIN
);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UnconditionLoopBreak extends Analyzer {

    public function analyze(): void {
        // foreach($a as $b) { $c++; continue; }
        $this->atomIs(self::LOOPS_ALL)
             ->outIs('BLOCK')
             ->outIs('EXPRESSION')
             ->atomIs(self::BREAKS)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NextMonthTrap extends Analyzer {
    public function analyze(): void {
        // strtotime('+ 1 month')
        $this->atomFunctionIs('\strtotime')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->regexIs('fullcode', '(\\\\+|-|\\\\\$)[0-9a-zA-Z_ ]+month')
             ->back('first');
        $this->prepareQuery();

        // strtotime('+ 1 month')
        $this->atomFunctionIs('\strtotime')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->regexIs('noDelimiter', '(?i)(?<!of )next month')
             ->back('first');
        $this->prepareQuery();

        // new Datetime('+ 1 month')
        $this->atomIs('New')
             ->outIs('NEW')
             ->fullnspathIs(array('\\datetime', '\\datetimeimmutable'))
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->regexIs('fullcode', '(\\\\+|-|\\\\\$)[0-9a-zA-Z_ ]+month')
             ->back('first');
        $this->prepareQuery();

        // strtotime('+ 1 month')
        $this->atomIs('New')
             ->outIs('NEW')
             ->fullnspathIs(array('\\datetime', '\\datetimeimmutable'))
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->regexIs('noDelimiter', '(?i)(?<!of )next month')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class AutoUnsetForeach extends Analyzer {
    public function analyze(): void {
        // foreach($a as $a) {}
        $this->atomIs('Foreach')
             ->outIs('SOURCE')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'source')
             ->back('first')
             ->outIs(array('INDEX', 'VALUE'))
             ->atomInsideNoDefinition('Variable')
             ->samePropertyAs('code', 'source')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class IdenticalOnBothSides extends Analyzer {
    public function analyze(): void {
        // $a && $a
        // $b == $b
        $this->atomIs(array('Comparison', 'Logical'))
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'left')
             ->inIs('LEFT')
             ->outIs('RIGHT')
             ->samePropertyAs('fullcode', 'left', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // substractions are done with IsZero
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class NoReferenceForTernary extends Analyzer {
    public function analyze(): void {
        // function &foo() { return $a ?? $b; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->is('reference', true)
             ->outIs('BLOCK')
             ->atomInsideNoDefinition('Return')
             ->outIs('RETURN')
             ->atomIs(array('Ternary', 'Coalesce'))
             ->back('first');
        $this->prepareQuery();

        // function foo() { $a = &$b; $c = rand() ?? $a; }
        $this->atomIs('Variable')
             ->is('reference', true)
             ->inIs('RIGHT')
             ->atomIs('Assignation')
             ->outIs('LEFT')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs(array('THEN', 'ELSE'))
             ->atomIs(array('Ternary', 'Coalesce'));
        $this->prepareQuery();

        // function foo(&$a) { 1 ?? $a ; }
        $this->atomIs('Variable')
             ->inIs(array('THEN', 'ELSE'))
             ->atomIs(array('Ternary', 'Coalesce'))
             ->as('results')
             ->back('first')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->is('reference', true)
             ->back('results');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class UnusedInheritedVariable extends Analyzer {
    public function analyze(): void {
        // function ($a) use ($b) { return $a; }
        $this->atomIs('Closure')
             ->outIs('USE')
             ->savePropertyAs('code', 'inherited')
             ->isUsed(0)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Exceptions;

use Exakat\Analyzer\Analyzer;

class UselessCatch extends Analyzer {
    public function analyze(): void {
        // try {} catch (Exception $e) { return 1; }
        $this->atomIs('Try')
             ->outIs('CATCH')
             ->outIs('BLOCK')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Return')
             ->outIs('RETURN')
             ->is('constant', true)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class DontUnsetProperties extends Analyzer {
    // unset($a->b);
    public function analyze(): void {
        $this->atomIs('Unset')
             ->outIs('ARGUMENT')
             ->atomIs(self::PROPERTIES)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->outIs('CAST')
             ->atomIs(self::PROPERTIES)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class StrtrArguments extends Analyzer {
    public function analyze(): void {
        // strtr($a, $b, '') is always useless
        $this->atomFunctionIs('\\strtr')
             ->outWithRank('ARGUMENT', 2)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->noDelimiterIs('')
             ->back('first');
        $this->prepareQuery();

        // strtr($a, 'ab', 'cde') has different size in arguments
        // strtr($a, 'abc', 'cd') has different size in arguments
        $this->atomFunctionIs('\\strtr')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->noDelimiterIsNot('')
             ->getStringLength('noDelimiter', 's1')
             ->inIs('ARGUMENT')
             ->outWithRank('ARGUMENT', 2)
             ->atomIs('String')
             ->hasNoOut('CONCAT')
             ->noDelimiterIsNot('')
             ->getStringLength('noDelimiter', 's2')
             ->raw('filter{s1 != s2}')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class MissingParenthesis extends Analyzer {
    public function analyze(): void {
        // -$a + $b
        $this->atomIs('Addition')
             ->codeIs('+')
             ->outIs('LEFT')
             ->atomIs('Sign')
             ->codeIs('-')
             ->outIs('SIGN')
             ->atomIsNot('Parenthesis')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Performances;

use Exakat\Analyzer\Analyzer;

class StrposTooMuch extends Analyzer {
    public function analyze(): void {
        // strpos($a, $b) === 0
        $this->atomFunctionIs(array('\\strpos', '\\stripos', '\\strrpos', '\\strripos'))
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs('Comparison')
             ->codeIs(array('==', '==='))
             ->outIs('RIGHT')
             ->has('intval')
             ->codeIs(array('0'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class TypehintedReferences extends Analyzer {
    public function analyze(): void {
        // function foo(object &$x)
        // function foo(X &$x)
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->is('reference', true)
             ->outIs('TYPEHINT')
             ->atomIsNot(array('Void', 'Scalartypehint'))
             ->back('first');
        $this->prepareQuery();

        // function &foo($x) : X
        $this->atomIs(self::FUNCTIONS_ALL)
             ->is('reference', true)
             ->outIs('RETURNTYPE')
             ->atomIsNot(array('Void', 'Scalartypehint'))
             ->back('first');
        $this->prepareQuery();

        // No need to check properties, it leads to a fatal error
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class CheckJson extends Analyzer {
    public function analyze(): void {
        // json_decode() (no checks)
        $this->atomFunctionIs(array('\\json_encode', '\\json_decode'))
             ->hasNoTryCatch()
             ->goToExpression()
             ->hasNoNextSibling('EXPRESSION')
             ->back('first');
        $this->prepareQuery();

        // json_decode() (no checks)
        $this->atomFunctionIs(array('\\json_encode', '\\json_decode'))
             ->analyzerIsNot('self')
             ->hasNoTryCatch()
             ->goToExpression()
             ->nextSibling('EXPRESSION')
             ->noFunctionInside(array('\\json_last_error', '\\json_last_error_msg'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Variables;

use Exakat\Analyzer\Analyzer;

class UndefinedVariable extends Analyzer {
    public function dependsOn(): array {
        return array('Functions/DynamicCode',
                     'Complete/PhpNativeReference',
                     'Complete/MakeFunctioncallWithReference',
                    );
    }

    public function analyze(): void {
        // function foo() { echo $b;}
        $this->atomIs('Variabledefinition')
             // not from eval or include
             // Not from extract
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_ALL)
                     ->analyzerIs('Functions/DynamicCode')
             )

             // Not from foreach
             ->not(
                $this->side()
                     ->outIs('DEFINITION')
                     ->inIs('VALUE')
                     ->atomIs('Foreach')
             )
             ->filter(
                $this->side()
                     ->outIs('DEFINITION')
                     ->atomIs('Variable')
                     ->is('isRead', true)
             )
             ->not(
                $this->side()
                     ->outIs('DEFINITION')
                     ->atomIs('Variable')
                     ->is('isModified', true)
             )

             ->outIs('DEFINITION');
        $this->prepareQuery();

        // function foo() { $b->c = 2;}
        $this->atomIs('Variabledefinition')
             // not from eval or include
             // Not from extract
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_ALL)
                     ->analyzerIs('Functions/DynamicCode')
             )

             ->filter(
                 $this->side()
                      ->outIs('DEFINITION')
                      ->atomIs(array('Variableobject', 'Variablearray'))
             )
            ->not(
                $this->side()
                     ->outIs('DEFINITION')
                     ->atomIs('Variable')
                     ->is('isModified', true)
            )
            ->outIs('DEFINITION')
            ->analyzerIsNot('self');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class ShouldYieldWithKey extends Analyzer {
    public function analyze(): void {
        //iterator_to_array( bad1( ) );
        // function bad1() { yield from generator(); yield from generator(); }
        $this->atomFunctionIs('\\iterator_to_array')
             ->outIs('ARGUMENT')
             ->inIs('DEFINITION')
             ->atomInsideNoDefinition('Yieldfrom')
             ->outIs('YIELD')
             ->inIs('DEFINITION')
             ->atomInsideNoDefinition('Yield')
             ->outIs('YIELD')
             ->atomIsNot('Keyvalue')
             ->back('first');
        $this->prepareQuery();

        // TODO : Yieldfrom may have several levels of yielding. Repeat is necessary
        // TODO : when Yieldfrom is alone, it should be OK (but not if there are mixed yield from and yield)
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Traits;

use Exakat\Analyzer\Analyzer;

class UselessAlias extends Analyzer {
    public function analyze(): void {
        // class x { use t { f as f; }}
        $this->atomIs('Usetrait')
             ->outIs('BLOCK')
             ->outIs('EXPRESSION')
             ->atomIs('As')
             ->as('results')
             ->outIs('AS')
             ->savePropertyAs('lccode', 'name')
             ->inIs('AS')
             ->outIs('NAME')
             ->outIsIE('METHOD')
             ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
             ->back('results');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class MissingSubpattern extends Analyzer {
     private $pregFunctions = array('\\preg_match_all',
                                    '\\preg_match',
                                    '\\preg_replace',
                                    '\\preg_replace_callback',
                                    '\\preg_relace_callback_array',
                                    );

    public function analyze(): void {

        //preg_match('/(a)b?/', 'adc', $r)
        $this->atomFunctionIs($this->pregFunctions)
             ->hasChildWithRank('ARGUMENT', 2) // subpatterns are captured
             // Also for preg_replace_* but that won't happen.
             ->not(
                $this->side()
                     ->outWithRank('ARGUMENT', 3)
                     ->atomInsideNoDefinition(array('Nsname', 'Identifier'))
                     ->fullnspathIs('\PREG_UNMATCHED_AS_NULL', self::CASE_SENSITIVE)
             )
             ->outWithRank('ARGUMENT', 0)
             ->has('noDelimiter')
             ->regexIs('noDelimiter', '\\\\)\\\\?[^\\\\(]*[^a-zA-Z][a-zA-Z]*\\$')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class AssigneAndCompare extends Analyzer {
    public function analyze(): void {
        // if ($a = strpos($b, $c) > 0)
        $this->atomIs('Assignation')
             ->hasNoIn('EXPRESSION')
             ->outIs('RIGHT')
             ->atomIs('Comparison')

             //Skip comparison that yield boolean
             ->not(
                $this->side()
                     ->codeIs(array('==', '==='))
                     ->outIs(array('LEFT', 'RIGHT'))
                     ->atomIs('Boolean')
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class TypehintMustBeReturned extends Analyzer {
    public function analyze(): void {
        // function foo() :A { return; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->fullnspathIsNot(array('\\void', '\\never')) // Void cannot be with other typehints
             ->back('first')
             ->isNot('abstract', true)
             // Not an empty block
             ->not(
                $this->side()
                     ->outIs('BLOCK')
                     ->atomIs('Void')
             )
             // Do not throw
             ->not(
                $this->side()
                     ->outIs('BLOCK')
                     ->atomInsideNoDefinition('Throw')
             )
             ->not(
                $this->side()
                     ->outIs('BLOCK')
                     ->atomInsideNoDefinition('Functioncall')
                     ->fullnspathIs(array('\\assert', '\\trigger_error'))
             )
             ->outIs('RETURNED')
             ->atomIs('Void')
             ->hasNoIn('RETURN')
             ->back('first');
        $this->prepareQuery();

        // function foo() : A { }
        // This is an extension of PHP checks
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->fullnspathIsNot(array('\\void', '\\never'))
             ->back('first')
             ->isNot('abstract', true)
             ->not(
                $this->side()
                     ->outIs('BLOCK')
                     ->atomIs('Void')
             )
             // Do not throw
             ->not(
                $this->side()
                     ->outIs('BLOCK')
                     ->atomInsideNoDefinition('Throw')
             )
             ->not(
                $this->side()
                     ->outIs('BLOCK')
                     ->atomInsideNoDefinition('Functioncall')
                     ->fullnspathIs(array('\\assert', '\\trigger_error'))
             )
             ->hasNoOut('RETURNED')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class IsNotFamily extends Analyzer {
    public function analyze(): void {
        // Staticmethodcall
        // Inside the class
        $this->atomIs('Staticmethodcall')
             ->hasClass()
             ->outIs('CLASS')
             ->atomIsNot(self::RELATIVE_CLASS)
             ->has('fullnspath')
             ->savePropertyAs('fullnspath', 'fnp')
             ->goToClass()
             ->atomIs('Class')

             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->atomIs('Class')
                             ->goToAllParents(self::INCLUDE_SELF)
                             ->fullnspathIs('fnp')
                     )
             )
             ->back('first');
        $this->prepareQuery();

        // Case of anonymous classes
        $this->atomIs('Staticmethodcall')
             ->hasClass()
             ->outIs('CLASS')
             ->atomIsNot(self::RELATIVE_CLASS)
             ->has('fullnspath')
             ->savePropertyAs('fullnspath', 'fnp')
             ->goToClass()
             ->atomIs('Classanonymous')
             ->back('first');
        $this->prepareQuery();

        // All non-in-class calls are OK
        $this->atomIs('Staticmethodcall')
             ->hasNoClassTrait();
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class CastingTernary extends Analyzer {
    public function analyze(): void {
        // $a = (string) $b ? 3 : 4;
        // $a = (string) $b ?: 4;
        // $a = (string) $b ?? 4;
        $this->atomIs('Cast')
             ->inIs(array('CONDITION', 'LEFT'))
             ->atomIs(array('Ternary', 'Coalesce'));
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class ConcatAndAddition extends Analyzer {
    public function analyze(): void {
        // "sum ". $a + $b
        $this->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->atomIs(array('Addition', 'Bitshift'))
             ->back('first');
        $this->prepareQuery();

        // $a + $b . "sum "
        // for PHP 7.4 and later
        $this->atomIs(array('Addition', 'Bitshift'))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Concatenation')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class IsGenerator extends Analyzer {
    public function analyze(): void {
        // function foo() { yield 3; }
        $this->atomIs(array('Yield', 'Yieldfrom'))
             ->goToFunction();
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class NoParent extends Analyzer {
    protected $phpVersion = '7.4-';

    public function analyze(): void {
        // class x { function foo() { parent::fooo(); }}
        $this->atomIs('Parent')
             ->goToFunction()
             ->inIs(array('METHOD', 'MAGICMETHOD'))
             ->atomIs(array('Class', 'Classanonymous')) // No traits...
             ->hasNoOut('EXTENDS')
             ->back('first')
             ->inIs();
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ShouldUseExplodeArgs extends Analyzer {
    public function analyze(): void {
        // $c = explode('a', $string); array_pop($c)
        $this->atomFunctionIs('\\explode')
             ->NoChildWithRank('ARGUMENT', 2)
             ->inIs('RIGHT')
             ->atomIs('Assignation')

             ->outIs('LEFT')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'name')
             ->inIs('LEFT')

             ->nextSibling('EXPRESSION')
             ->functioncallIs('\\array_pop')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Variable')
             ->samePropertyAs('code', 'name')
             ->back('first');
        $this->prepareQuery();

        // $c = explode('a', $string); array_slice($c, 0, -3)
        $this->atomFunctionIs('\\explode')
             ->NoChildWithRank('ARGUMENT', 2)
             ->inIs('RIGHT')
             ->atomIs('Assignation')

             ->outIs('LEFT')
             ->atomIs('Variable')
             ->savePropertyAs('code', 'name')
             ->inIs('LEFT')

             ->nextSibling('EXPRESSION')
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->functioncallIs('\\array_slice')
             ->outWithRank('ARGUMENT', 2)
             ->atomIs('Integer', self::WITH_CONSTANTS)
             ->isLess('intval', 0)
             ->back('first');
        $this->prepareQuery();

        // list($a, $b, ) = explode('a', $string);
        $this->atomFunctionIs('\\explode')
             ->NoChildWithRank('ARGUMENT', 2)
             ->inIs('RIGHT')
             ->atomIs('Assignation')

             ->outIs('LEFT')
             ->atomIs('List')
             ->outWithRank('ARGUMENT', 'last')
             ->atomIs('Void')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Performances;

use Exakat\Analyzer\Analyzer;

class UseArraySlice extends Analyzer {
    public function analyze(): void {
        // while($cdg) { array_pop($c); }
        $this->atomIs(self::LOOPS_ALL)
             ->outIs('BLOCK')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->functioncallIs(array('\\array_shift',
                                    '\\array_pop',
                                   ))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class CoalesceAndConcat extends Analyzer {
    public function analyze(): void {
        // 'a' . $b ?? 'c'
        $this->atomIs('Coalesce')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Concatenation')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Interfaces;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\CollectMethods;

class IsNotImplemented extends Analyzer {
    public function analyze(): void {
        // interface i { function i() {}}
        // class c implements i {       }
        $this->atomIs('Interface')
             ->collectMethods('interfaceMethods')
             ->raw('filter{interfaceMethods.size() > 0;}')
             ->goToAllChildren(self::EXCLUDE_SELF)
             ->atomIs(self::CLASSES_ALL)
             ->isNot('abstract', true)
             ->collectMethods('classMethods', CollectMethods::METHOD_CONCRETE)
             ->raw('filter{interfaceMethods.size() > classMethods.size() || !classMethods.containsAll(interfaceMethods);}');
        $this->prepareQuery();

        // todo : check for cross over interfaces
        // warning : methods may be spread over multiple classes
        // interface i { i1, i2} class a { function i1()} class b implement i { function i2()}
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Interfaces;

use Exakat\Analyzer\Analyzer;

class CantImplementTraversable extends Analyzer {
    public function analyze(): void {
        // class x implements traversable
        $this->atomIs(self::STATIC_NAMES)
             ->fullnspathIs('\traversable')
             ->inIs(array('EXTENDS', 'IMPLEMENTS'));
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class MergeIfThen extends Analyzer {
    public function analyze(): void {
        // if () { if () { }}
        $this->atomIs('Ifthen')
             ->outIs('THEN')
             ->is('count', 1)
             ->outIs('EXPRESSION')
             ->atomIs('Ifthen')
             ->back('first')
             ->hasNoOut('ELSE');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class NotEqual extends Analyzer {
    public function analyze(): void {
        // !$a == 'b'
        $this->atomIs('Comparison')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Not')
             ->back('first')
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('String', 'Integer'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;
use Exakat\Data\Methods;
use Exakat\Query\DSL\FollowParAs;

class WrongTypeForNativeFunction extends Analyzer {
    public function analyze(): void {
        $types = array('float'  => array('Integer', 'Float'),
                       'int'    => array('Integer'),
                       'string' => self::STRINGS_LITERALS,
                       'array'  => array('Arrayliteral'),
                       'bool'   => array('Boolean', 'Bitoperation', 'Logical', 'Comparison'),
                      );

        $castTypes = array('float'  => array('T_DOUBLE_CAST', 'T_INT_CAST'),
                           'int'    => 'T_INT_CAST',
                           'string' => 'T_STRING_CAST',
                           'array'  => 'T_ARRAY_CAST',
                           'bool'   => 'T_BOOL_CAST',
                      );

        $returntypes = array();
        foreach($types as $type => $atoms) {
            $returntypes[$type] = $this->methods->getFunctionsByReturnType($type, Methods::LOOSE);
        }
        $returntypes['null'] = $this->methods->getFunctionsByReturnType('null', Methods::LOOSE);
        $returntypes['false'] = $this->methods->getFunctionsByReturnType('false', Methods::LOOSE);

        $returnOtherTypes = array();
        foreach($returntypes as $type => $functions) {
            $r2 = $returntypes;
            unset($r2[$type]);

            $returnOtherTypes[$type] = array_unique(array_merge(...array_values($r2)));
        }

        foreach($types as $type => $atoms) {
            $ini = $this->methods->getFunctionsByArgType($type, Methods::STRICT);

            if (empty($ini)) {
                continue;
            }

            foreach($ini as $rank => $functions) {
                if (empty($functions)) { continue; }

                // class x { string $id; function foo() { array_map($this->id, '') ; }
                $this->atomFunctionIs($functions)
                     ->analyzerIsNot('self')
                     ->outWithRank('ARGUMENT', (int) $rank)
                     ->atomIs(array('Member', 'Staticproperty'))
                     ->inIs('DEFINITION')
                     ->atomIs('Propertydefinition')
                     ->inIs('PPP')
                     ->collectTypehints('typehints')
                     ->not(
                        $this->side()
                             ->outIs('TYPEHINT')
                             ->atomIs('Void')
                     )
                     ->raw('filter{!("\\\\' . $type . '" in typehints);}')
                     ->back('first');
                $this->prepareQuery();

                // foo($arg) { array_map($arg, '') ; }
                $this->atomFunctionIs($functions)
                     ->analyzerIsNot('self')
                     ->outWithRank('ARGUMENT', (int) $rank)
                     ->atomIs('Variable')
                     ->inIs('DEFINITION')
                     ->inIs('NAME')
                     ->collectTypehints('typehints')
                     ->not(
                        $this->side()
                             ->outIs('TYPEHINT')
                             ->atomIs('Void')
                     )
                     ->raw('filter{!("\\\\' . $type . '" in typehints);}')
                     ->back('first');
                $this->prepareQuery();

                // array_map(STRING, '')
                // raw expressions
                $this->atomFunctionIs($functions)
                     ->analyzerIsNot('self')
                     ->outWithRank('ARGUMENT', $rank)
                     ->followParAs(FollowParAs::FOLLOW_NONE)
                     ->as('results')
                     ->atomIsNot($atoms, self::WITH_CONSTANTS)
                     ->not(
                        $this->side()
                             ->atomIs('Cast')
                             ->tokenIs($castTypes[$type])
                     )
                     ->back('results')
                     ->atomIsNot(array_merge(self::CALLS, self::CONTAINERS, array('Void', 'Identifier', 'Nsname', 'Staticconstant', 'Coalesce', 'Ternary')))
                     ->back('first');
                $this->prepareQuery();

                // native functions
                // substr(rand(), 1)
                $this->atomFunctionIs($functions)
                     ->analyzerIsNot('self')
                     ->outWithRank('ARGUMENT', (int) $rank)
                     ->as('results')
                     ->followParAs(FollowParAs::FOLLOW_NONE)
                     ->atomIs('Functioncall', self::WITH_VARIABLES)
                     ->is('isPhp', true)
                     ->fullnspathIs($returnOtherTypes[$type])

                     // Special case for false, inside a ?:
                     ->not(
                        $this->side()
                             ->fullnspathIs($returntypes['bool'])
                             ->inIs('CONDITION')
                             ->atomIs('Ternary')
                             ->outIs('THEN')
                             ->atomIs('Void')
                     )

                     // Special case for null, inside a ??
                     ->not(
                        $this->side()
                             ->fullnspathIs($returntypes['null'])
                             ->inIs('LEFT')
                             ->atomIs('Coalesce')
                     )
                     ->back('results')
                     ->atomIsNot(array_merge(self::CONTAINERS, array('Void', 'Identifier', 'Nsname')))
                     ->back('first');
                $this->prepareQuery();

                // custom functions
                // function foo() : int {}; substr(foo(), 1)
                $this->atomFunctionIs($functions)
                     ->analyzerIsNot('self')
                     ->outWithRank('ARGUMENT', (int) $rank)
                     ->atomIs(self::CALLS, self::WITH_VARIABLES)
                     ->inIs('DEFINITION')
                     ->outIs('RETURNTYPE')
                     ->atomIsNot('Void')
                     ->fullnspathIsNot('\\' . $type)
                     ->back('first');
                $this->prepareQuery();
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class AddZero extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        // $x += 0
        $this->atomIs('Assignation')
             ->codeIs(array('+=', '-='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->outIs('RIGHT')
             ->followParAs(FollowParAs::FOLLOW_PARAS_ONLY)
             ->atomIsNot(array('Ternary', 'Coalesce'))
             ->atomIsNot('Variable')
             ->atomIs(array('Integer', 'Null', 'Boolean'), self::WITH_CONSTANTS)
             ->is('intval', 0)
             ->back('first');
        $this->prepareQuery();

        // 0 + ($c = 2)
        $this->atomIs('Addition')
             ->outIs(array('LEFT', 'RIGHT'))
             ->followParAs(FollowParAs::FOLLOW_PARAS_ONLY)
             ->atomIs(array('Integer', 'Null', 'Boolean'), self::WITH_CONSTANTS)
             ->is('intval', 0)
             ->back('first');
        $this->prepareQuery();

        // $a = 0; $c = $a + 2;
        $this->atomIs('Assignation')
             ->codeIs('=')
             ->outIs('RIGHT')
             ->followParAs(FollowParAs::FOLLOW_PARAS_ONLY)
             ->atomIs(array('Integer', 'Null', 'Boolean'), self::WITH_CONSTANTS)
             ->is('intval', 0)
             ->back('first')
             ->outIs('LEFT')
             ->atomIs('Variable')
             ->savePropertyAs('fullcode', 'varname')
             ->back('first')
             ->nextSibling()
             ->atomIsNot(array('Function', 'Class', 'Trait', 'Interface', 'Dowhile', 'While', 'Foreach', 'For'))
             ->atomInsideNoDefinition('Addition')
             ->as('results')
             ->analyzerIsNot('self')
             ->outIs(array('LEFT', 'RIGHT'))
             ->samePropertyAs('fullcode', 'varname')
             ->back('results');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Arrays;

use Exakat\Analyzer\Analyzer;

class MultipleIdenticalKeys extends Analyzer {
    protected $arrayMaxSize = 15000;

    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        // array('a' => 1, 'b' = 2)
        $this->atomIs('Arrayliteral')
             ->is('constant', true)
             ->isLess('count', $this->arrayMaxSize)

            // Check that the array is a hash
             ->not(
                $this->side()
                     ->outIs('ARGUMENT')
                     ->atomIsNot('Keyvalue')
             )

             ->filter(
                $this->side()
                     ->initVariable('counts', '[:]')
                     ->outIs('ARGUMENT')
                     ->atomIs('Keyvalue')
                     ->outIs('INDEX')
                     ->atomIs(array('String', 'Heredoc', 'Concatenation', 'Integer', 'Float', 'Boolean', 'Null', 'Staticclass'), self::WITH_CONSTANTS)
                     ->raw('or(has("intval"), has("noDelimiter"))')
                     ->raw(<<<'GREMLIN'
sideEffect{ 
    if (it.get().label() in ["String", "Heredoc", "Concatenation", "Staticclass"] ) { 
        k = it.get().value("noDelimiter"); 
        if (k.isInteger()) {
            k = k.toInteger();
            
            if (k.toString().length() != it.get().value("noDelimiter").length()) {
                k = it.get().value("noDelimiter"); 
            }
        }
    } 
    else { 
        k = it.get().value("intval"); 
    } 

    if (counts[k] == null) { 
        counts[k] = 1; 
    } else { 
        counts[k]++; 
    }
}

.filter{ counts.findAll{it.value > 1}.size() > 0; }
GREMLIN
)
             )

             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassMethodRemoteDefinition extends Complete {
    public function dependsOn(): array {
        return array('Complete/SetParentDefinition',
                    );
    }

    public function analyze(): void {
        // class x { function foo() {}} x::foo();
        $this->atomIs(array('Staticmethodcall', 'Methodcall'), self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs(array('CLASS', 'OBJECT'))
              // Handles variables as object
              ->optional(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('DEFAULT')
                     ->outIs('NEW')
              )
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_TRAITS, self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // class x { use t} trait t {function foo() {}} x::foo();
        $this->atomIs('Staticmethod', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs(array('Identifier', 'Nsname'), self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->atomIs('Trait', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();
/*
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
              ->as('method')
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->has('fullnspath')
              ->savePropertyAs('fullnspath', 'fnp')
              ->filter(
                    $this->side()
                         ->goToClass()
                         ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
                         ->goToAllParents(self::INCLUDE_SELF)
                         ->samePropertyAs('fullnspath', 'fnp', self::CASE_SENSITIVE)
              )
              ->inIs('DEFINITION')
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'method');
        $this->prepareQuery();
        */
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

use Exakat\Query\DSL\FollowParAs;

class CreateDefaultValues extends Complete {
    public function dependsOn(): array {
        return array( 'Complete/OverwrittenProperties',
                    );
    }

    public function analyze(): void {
        // Link initial values for containers
        $this->atomIs(array('Variabledefinition',
                            'Virtualproperty',
                            'Propertydefinition',
                            'Parametername',
                            ), self::WITHOUT_CONSTANTS)
             ->outIsIE('NAME')
             ->savePropertyAs('fullcode', 'left')
             ->back('first')

             ->outIs('DEFINITION')
             ->inIs('LEFT')
             ->atomIs('Assignation', self::WITHOUT_CONSTANTS)
             ->codeIs(array('=', '??='), self::TRANSLATE, self::CASE_SENSITIVE) // can't accept .=, +=, etc.

             // doesn't use self : $a = $a + 1 is not a default value
             ->not(
                $this->side()
                     ->outIs('RIGHT')
                     ->atomInsideNoDefinition(self::VARIABLES_ALL)
                     ->inIs('DEFINITION')
                     ->inIsIE('NAME')
                     ->samePropertyAs('fullcode', 'left')
                     ->back('first')
                     ->atomIs(array('Variabledefinition',
                                    'Parametername',
                                    ), self::WITHOUT_CONSTANTS)
             )
             ->outIs('RIGHT')
             ->followParAs(FollowParAs::FOLLOW_NONE)

             // 'Variableobject', 'Variablearray' are never on the right side of an assignation (not directly)
             ->not(
                $this->side()
                     ->atomIs('Variable')
                     ->inIs('DEFINITION')
                     ->inIsIE('NAME')
                     ->raw('is(eq("first"))')
             )

             // avoid multiple definitions of DEFAULT (although, but why ?)
             ->not(
                $this->side()
                     ->inIs('DEFAULT')
             )
             ->addEFrom('DEFAULT', 'first');
        $this->prepareQuery();

        // With comparisons
        $this->atomIs(array('Variabledefinition',
                            'Virtualproperty',
                            'Propertydefinition',
                            'Parametername',
                            ), self::WITHOUT_CONSTANTS)
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs('Comparison', self::WITHOUT_CONSTANTS)
             ->codeIs(array('==', '!=', '===', '!==', ), self::TRANSLATE, self::CASE_SENSITIVE)
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Integer', 'String'), self::WITH_CONSTANTS)
             ->addEFrom('DEFAULT', 'first');
        $this->prepareQuery();

        // With switch/match
        $this->atomIs(array('Variabledefinition',
                            'Virtualproperty',
                            'Propertydefinition',
                            'Parametername',
                            ), self::WITHOUT_CONSTANTS)
             ->outIs('DEFINITION')
             ->inIs('CONDITION')
             ->atomIs(self::SWITCH_ALL)
             ->outIs('CASES')
             ->outIs('EXPRESSION')
             ->atomIs('Case')
             ->outIs('CASE')
             ->atomIs(array('Integer', 'String'), self::WITH_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFAULT')
                     ->raw('is(neq("first"))')
             )
             ->addEFrom('DEFAULT', 'first');
        $this->prepareQuery();

        // With foreach($a as $v)
        // With foreach($a as $k => $v)
        // This is done with Complete/CreateForeachDefault

        // propagate virtualproperties to original definition
        // This one must be the final of this analysis
        $this->atomIs(array('Propertydefinition'), self::WITHOUT_CONSTANTS)
             ->inIs('OVERWRITE')
             ->outIs('DEFAULT')
             ->not(
                $this->side()
                     ->inIs('DEFAULT')
                     ->raw('is(neq("first"))')
             )
             ->atomIsNot('Void')
             ->addEFrom('DEFAULT', 'first');
        $this->prepareQuery();

        // for global values
        $this->atomIs(array('Virtualglobal'), self::WITHOUT_CONSTANTS)
             ->outIs('DEFINITION')
             ->inIs('DEFINITION')
             ->atomIs('Variabledefinition')
             ->outIs('DEFAULT')
             ->addEFrom('DEFAULT', 'first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class ConstantStrangeNames extends Analyzer {
    public function dependsOn(): array {
        return array('Constants/Constantnames',
                    );
    }

    public function analyze(): void {
        // define('BD$', 1);
        $this->atomIs('Identifier')
             ->hasNoOut('CONCAT')
             ->not(
                $this->side()
                     ->inIs('NAME')
                     ->atomIs('Const')
             )
             ->analyzerIs('Constants/Constantnames')
             ->regexIsNot('noDelimiter', '^(\\\\\\\\?)[a-zA-Z_\\\\x7f-\\\\xff][a-zA-Z0-9_\\\\x7f-\\\\xff]*\\$')
             // simple constant name
             ->regexIsNot('noDelimiter', '^(\\\\\\\\?)([a-zA-Z_\\\\x7f-\\\\xff][a-zA-Z0-9_\\\\x7f-\\\\xff]*\\\\\\\\)+[a-zA-Z_\\\\x7f-\\\\xff][a-zA-Z0-9_\\\\x7f-\\\\xff]*\\$');
             // \\\\\\\\ is equivalent to \\ (two slashes) in the final regex.
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ExitUsage extends Analyzer {
    public function dependsOn(): array {
        return array('Structures/NoDirectAccess',
                     'Files/IsCliScript',
                    );
    }

    public function analyze(): void {
        // while (list($a, $b) = each($c)) {}
        $this->atomIs('Exit')
             ->goToInstruction('Ifthen')
             ->analyzerIsNot('Structures/NoDirectAccess')
             ->goToFile()
             ->analyzerIsNot('Files/IsCliScript')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Exit')
             ->hasNoInstruction('Ifthen')
             ->goToFile()
             ->analyzerIsNot('Files/IsCliScript')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class MultiplyByOne extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        $atoms = array('String', 'Integer', 'Boolean', 'Float', 'Identifier', 'Nsname', 'Assignation', 'Parenthesis', 'Multiplication');

        // $x *= 1;
        $this->atomIs('Assignation')
             ->codeIs(array('*=', '/=', '%=', '**='))
             ->outIs('RIGHT')
             ->followParAs(FollowParAs::FOLLOW_PARAS_ONLY)
             ->atomIsNot(array('Ternary', 'Coalesce'))
             ->atomIsNot('Variable')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->is('intval', 1)
             ->regexIs('noDelimiter', '^1\\\\.?0*\\$')
             ->back('first');
        $this->prepareQuery();

        // $x = $y * 1
        $this->atomIs('Multiplication')
             ->codeIs('*')
             ->outIs(array('LEFT', 'RIGHT'))
             ->followParAs(FollowParAs::FOLLOW_PARAS_ONLY)
             ->atomIsNot(array('Ternary', 'Coalesce'))
             ->atomIsNot('Variable')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->is('intval', 1)
             ->regexIs('noDelimiter', '^1\\\\.?0*\\$')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Multiplication')
             ->codeIs(array('/', '%'))
             ->outIs('RIGHT')
             ->followParAs(FollowParAs::FOLLOW_PARAS_ONLY)
             ->atomIsNot(array('Ternary', 'Coalesce'))
             ->atomIsNot('Variable')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->is('intval', 1)
             ->regexIs('noDelimiter', '^1\\\\.?0*\\$')
             ->back('first');
        $this->prepareQuery();

        // $b * 12 / 12
        $this->atomIs('Multiplication')
             ->codeIs('/')
             ->outIs('RIGHT')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->isNot('intval', 1)
             ->savePropertyAs('intval', 'operand')
             ->back('first')

             ->outIs('LEFT')
             ->atomIs('Multiplication')
             ->codeIs('*')
             ->outIs('RIGHT')
             ->samePropertyAs('intval', 'operand')

             ->back('first');
        $this->prepareQuery();

        // $b / 12 * 12
        $this->atomIs('Multiplication')
             ->codeIs('*')
             ->outIs('RIGHT')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->isNot('intval', 1)
             ->savePropertyAs('intval', 'operand')
             ->back('first')

             ->outIs('LEFT')
             ->atomIs('Multiplication')
             ->codeIs('/')
             ->outIs('RIGHT')
             ->samePropertyAs('intval', 'operand')

             ->back('first');
        $this->prepareQuery();

        // $x = $y ** 1
        $this->atomIs('Power')
             ->outIs('RIGHT')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->is('intval', 1)
             ->regexIs('noDelimiter', '^1\\\\.?0*\\$')
             ->back('first');
        $this->prepareQuery();

        // 1 ** $a;
        $this->atomIs('Power')
             ->outIs('LEFT')
             ->atomIs($atoms, self::WITH_CONSTANTS)
             ->is('intval', 1)
             ->regexIs('noDelimiter', '^1\\\\.?0*\\$')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class CreateMagicProperty extends Complete {
    public function dependsOn(): array {
        return array('Complete/OverwrittenProperties',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                     'Complete/CreateDefaultValues',
                     'Complete/SetClassRemoteDefinitionWithLocalNew',
                    );
    }

    public function analyze(): void {

        // Missing : typehinted properties, return typehint, clone

        // link to __get
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Propertydefinition')
             )
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('OVERWRITE')
                     ->atomIs('Propertydefinition')
             )
             ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'This'), self::WITHOUT_CONSTANTS)
             ->inIs('DEFINITION') // Good enough for This
             ->optional(          // For arguments
                $this->side()
                     ->inIs('NAME')
                     ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
                     ->outIs('TYPEHINT')
                     ->inIs('DEFINITION')
             )
             ->optional(  // for local variables
                $this->side()
                     ->outIs('DEFAULT')
                     ->atomIs('New')
                     ->outIs('NEW')
                     ->inIs('DEFINITION')
             )

            // In case we are in an interface
             ->optional(
                $this->side()
                     ->atomIs('Interface', self::WITHOUT_CONSTANTS)
                     ->outIs('DEFINITION')
                     ->inIs('IMPLEMENTS')
             )

             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('MAGICMETHOD')
             ->outIs('NAME')
             ->codeIs('__get', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->inIs('NAME')

             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // link to __set
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->is('isModified', true)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Propertydefinition')
             )
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('OVERWRITE')
                     ->atomIs('Propertydefinition')
             )
             ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'This'), self::WITHOUT_CONSTANTS)
             ->inIs('DEFINITION') // Good enough for This
             ->optional(          // For arguments
                $this->side()
                     ->inIs('NAME')
                     ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
                     ->outIs('TYPEHINT')
                     ->inIs('DEFINITION')
             )

            // In case we are in an interface
             ->optional(
                $this->side()
                     ->atomIs('Interface', self::WITHOUT_CONSTANTS)
                     ->outIs('DEFINITION')
                     ->inIs('IMPLEMENTS')
             )

             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('MAGICMETHOD')
             ->outIs('NAME')
             ->codeIs('__set', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // isset($this->a)
        // links to __isset
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Propertydefinition')
             )
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('OVERWRITE')
                     ->atomIs('Propertydefinition')
             )
             ->inIs('ARGUMENT')
             ->atomIs('Isset')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'This'), self::WITHOUT_CONSTANTS)
             ->optional(
                $this->side()
                     ->inIs('DEFINITION')
                     ->inIs('NAME')
                     ->outIs('TYPEHINT')
             )
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('MAGICMETHOD')
             ->outIs('NAME')
             ->codeIs('__isset', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // unset($this->a)
        // links to __unset
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Propertydefinition')
             )
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('OVERWRITE')
                     ->atomIs('Propertydefinition')
             )
             ->inIs('ARGUMENT')
             ->atomIs('Unset')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'This'), self::WITHOUT_CONSTANTS)
             ->optional(
                $this->side()
                     ->inIs('DEFINITION')
                     ->inIs('NAME')
                     ->outIs('TYPEHINT')
             )
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('MAGICMETHOD')
             ->outIs('NAME')
             ->codeIs('__unset', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // unset() $this->a
        // links to __unset
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Propertydefinition')
             )
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('OVERWRITE')
                     ->atomIs('Propertydefinition')
             )
             ->inIs('CAST')
             ->atomIs('Cast')
             ->tokenIs('T_UNSET_CAST')
             ->back('first')

              ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'This'), self::WITHOUT_CONSTANTS)
             ->optional(
                $this->side()
                     ->inIs('DEFINITION')
                     ->inIs('NAME')
                     ->outIs('TYPEHINT')
             )
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('MAGICMETHOD')
             ->outIs('NAME')
             ->codeIs('__unset', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // links to __invoke
        $this->atomIs('Magicmethod', self::WITHOUT_CONSTANTS)
             ->outIs('NAME')
             ->codeIs('__invoke', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first')

             ->inIs('MAGICMETHOD')
             ->outIs('DEFINITION')
             ->inIs('NEW')
             ->inIs('RIGHT')
             ->atomIs('Assignation')
             ->outIs('LEFT')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Functioncall')
             ->addEFrom('DEFINITION', 'first');
        $this->prepareQuery();

        // $this($a, $b);
        $this->atomIs('Magicmethod', self::WITHOUT_CONSTANTS)
             ->outIs('NAME')
             ->codeIs('__invoke', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first')

             ->inIs('MAGICMETHOD')
             ->outIs('DEFINITION')
             ->atomIs('This')
             ->inIs('NAME')
             ->atomIs('Functioncall')
             ->addEFrom('DEFINITION', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class ConstantUsage extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        // Nsname that is not used somewhere else
        $this->atomIs('Nsname')
             ->has('line')
             ->hasNoIn(array('NEW', 'USE', 'NAME', 'EXTENDS', 'IMPLEMENTS', 'CLASS', 'CONST', 'TYPEHINT', 'RETURNTYPE',
                             'FUNCTION', 'GROUPUSE'));
        $this->prepareQuery();

        // Identifier that is not used somewhere else
        $this->atomIs('Identifier')
             ->has('line')
             ->hasNoIn(array('NEW', 'USE', 'NAME', 'CONSTANT', 'MEMBER', 'TYPEHINT', 'INSTEADOF', 'METHOD', 'TYPEHINT', 'RETURNTYPE',
                             'CLASS', 'EXTENDS', 'IMPLEMENTS', 'CLASS', 'AS', 'VARIABLE', 'FUNCTION', 'CONST', 'GROUPUSE'));
        $this->prepareQuery();

        // special case for Boolean and Null
        $this->atomIs(array('Boolean', 'Null'))
                     ->has('line');
        $this->prepareQuery();

        // defined('constant') : then the string is a constant
        $this->atomFunctionIs(array('\defined', '\constant'))
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS);
        $this->prepareQuery();

        // Const outside a class
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class MakeClassConstantDefinition extends Complete {
    public function dependsOn(): array {
        return array('Complete/SetParentDefinition',
                    );
    }

    public function analyze(): void {
        // X::Constante -> class X { const Constante}
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->outIs('CONSTANT')
             ->savePropertyAs('code', 'name')
             ->back('first')
             ->outIs('CLASS')
             ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static'), self::WITHOUT_CONSTANTS)
             ->inIs('DEFINITION')
             ->atomIs(array('Class', 'Classanonymous', 'Interface'), self::WITHOUT_CONSTANTS)
             ->goToAllParents(self::INCLUDE_SELF)
             ->outIs('CONST')
             ->atomIs('Const', self::WITHOUT_CONSTANTS)
             ->outIs('CONST')
             ->outIs('NAME')
             ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // static::Constante -> class { const Constante}
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Static', self::WITHOUT_CONSTANTS)

              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Classanonymous', 'Interface'), self::WITHOUT_CONSTANTS)
              ->goToAllChildren(self::EXCLUDE_SELF)
              ->outIs('CONST')
              ->atomIs('Const', self::WITHOUT_CONSTANTS)
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // X::Constante -> class X { const Constante} non-private
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static', 'Parent'), self::WITHOUT_CONSTANTS)
              ->savePropertyAs('fullnspath', 'classe')
              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Classanonymous', 'Interface'), self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::EXCLUDE_SELF)
              ->outIs('CONST')
              ->atomIs('Const', self::WITHOUT_CONSTANTS)
              ->isNot('visibility', 'private')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // parent::Constante -> class { const Constante}
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parent', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('fullnspath', 'classe')
              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Classanonymous', 'Interface'), self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->atomIs('Const', self::WITHOUT_CONSTANTS)
              ->isNot('visibility', 'private')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // x $a; $a::Constante -> class x { const Constante}
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs(array('Variable', 'Member', 'Staticproperty'))
              ->goToTypehint()
              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Classanonymous', 'Interface'), self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->atomIs('Const', self::WITHOUT_CONSTANTS)
              ->isNot('visibility', 'private')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');

        $this->prepareQuery();

        // @todo : handle the cases with PHP / stub definitions
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class IsExtClass extends Analyzer {

    public function dependsOn(): array {
        return array('Classes/ClassUsage',
                    );
    }

    public function analyze(): void {
        // function foo() : \Generator {}
        // function foo() : \Generator {}
        $this->analyzerIs('Classes/ClassUsage')
             ->hasNoIn('DEFINITION')
             ->isAnyOf(array('isExt', 'isPhp'), true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2018 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class UndefinedFunctions extends Analyzer {
    public function analyze(): void {
        // foo(); (no function foo())
        $this->atomIs('Functioncall')
             ->has('fullnspath')
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true)
             ->hasNoFunctionDefinition()
             ->isNotIgnored();
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Variables;

use Exakat\Analyzer\Analyzer;

class VariableUsedOnceByContext extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/CreateCompactVariables',
                     'Functions/VariableArguments',
                     'Variables/SelfTransform',
                     'Variables/Blind',
                    );
    }

    public function analyze(): void {
        // global variables
        $this->atomIs('File')
             ->outIs('DEFINITION')
             ->atomIs('Variabledefinition')
             ->isUsed(1)
             ->outIs('DEFINITION');
        $this->prepareQuery();

        // argument by function
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->isNot('abstract', true)
             ->outIs(array('ARGUMENT', 'USE'))
             ->outIs('NAME')
             ->isUsed(0);
        $this->prepareQuery();

        // Normal variables and inherited functions from closures
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('DEFINITION')
             ->atomIs('Variabledefinition')
             ->filter(
                $this->side()
                     ->outIs('DEFINITION')
                     ->atomIs(array('Variable', 'Variableobject', 'Variablearray', 'Parameter', 'String'))
                     ->analyzerIsNot('Variables/SelfTransform')
                     ->raw('count().is(eq(1))')
             )
             ->not(
                $this->side()
                     ->outIs('DEFINITION')
                     ->atomIs(array('Variable', 'Variableobject', 'Variablearray'))
                     ->analyzerIs('Variables/Blind')
             )
             // This Variabledefinition is not for a static variable
             ->not(
                $this->side()
                     ->outIs('DEFINITION')
                     ->hasIn('STATIC')
             )
             ->outIs('DEFINITION');
        $this->prepareQuery();

        // Static, global variables may be reused during a new call
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->outIs('DEFINITION')
             ->atomIs(array('Globaldefinition', 'Staticdefinition'))
             ->isUsed(0)
             ->outIs('DEFINITION');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class OrDie extends Analyzer {
    public function dependsOn(): array {
        return array('Structures/NoDirectAccess');
    }

    public function analyze(): void {
        $this->atomIs('Logical')
             ->analyzerIsNot('Structures/NoDirectAccess')
             ->codeIs(array('or', '||'))
             ->outIs('RIGHT')
             ->atomIs('Exit')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class MustReturn extends Analyzer {
    public function dependsOn(): array {
        return array('Functions/CantUse',
                    );
    }

    public function analyze(): void {
        // class foo { function __callStatic($name, $foo) { $name; } }
        $this->atomIs('Magicmethod')
             ->isNot('abstract', true)
             ->hasClassTrait()
             ->outIs('NAME')
             ->codeIs(array('__call',
                            '__callStatic',
                            '__get',
                            '__isset',
                            '__sleep',
                            '__toString',
                            '__set_state',
                            '__debugInfo',
                            ), self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first')
             ->analyzerIsNot('Functions/CantUse')
             ->outIs('RETURNED')
             ->atomIs('Void')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Magicmethod')
             ->isNot('abstract', true)
             ->hasClassTrait()
             ->outIs('NAME')
             ->codeIs(array('__call',
                            '__callStatic',
                            '__get',
                            '__isset',
                            '__sleep',
                            '__toString',
                            '__set_state',
                            '__debugInfo',
                            ), self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first')
             ->analyzerIsNot('Functions/CantUse')
             ->hasNoOut('RETURNED');
        $this->prepareQuery();

        // function foo() : type { /* no return */ } (except with void)
        $this->atomIs(array('Function', 'Closure', 'Method', 'Arrowfunction'))
             ->isNot('abstract', true)
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->fullnspathIsNot('\\void')
             ->back('first')
             ->outIs('RETURNED')
             ->atomIs('Void')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(array('Function', 'Closure', 'Method', 'Arrowfunction'))
             ->isNot('abstract', true)
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->fullnspathIsNot('\\void')
             ->back('first')
             ->hasNoOut('RETURNED');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class DefinedProperty extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/VariableTypehint',
                     'Complete/IsStubStructure',
                     'Complete/IsExtStructure',
                     'Complete/IsPhpStructure',
                     'Complete/OverwrittenProperties',
                    );
    }

    public function analyze(): void {
        // locally defined
        // defined in local class (private included)
        $this->atomIs(array('Member', 'Staticproperty'))
             ->inIs('DEFINITION')
             ->atomIs('Propertydefinition')
             ->back('first');
        $this->prepareQuery();

        // defined in local class (private included)
        $this->atomIs(array('Member', 'Staticproperty'))
             ->isAnyOf(array('isPhp', 'isExt', 'isStub'), true);
        $this->prepareQuery();

        // defined in parent class
        $this->atomIs(array('Member', 'Staticproperty'))
             ->analyzerIsNot('self')
             ->inIs('DEFINITION')
             ->atomIs('Virtualproperty')
             ->outIs('OVERWRITE')
             ->atomIs('Propertydefinition')
             ->not(
                $this->side()
                     ->inIs('PPP')
                     ->is('visibility', 'private')
                     ->inIs('PPP')
                     ->atomIs('Class')
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class UseConstantAsArguments extends Analyzer {
    public function analyze(): void {
        $functions = $this->loadJson('php_constant_arguments.json');

        //alternative : one of the constants or nothing
        $positionsWithConstants = array();
        foreach($functions->alternative as $position => $functionsList) {
            foreach(array_keys((array) $functionsList) as $function) {
                $fqn = makeFullNsPath($function);

                array_collect_by($positionsWithConstants, $fqn, (int) $position);
            }
        }

        // Not a PHP constant
        $this->atomFunctionIs(array_keys($positionsWithConstants))
             ->analyzerIsNot('self')
             ->savePropertyAs('fullnspath', 'fqn')
             ->outIs('ARGUMENT')
             ->isHash('rank', $positionsWithConstants, 'fqn')
             ->atomIs(self::CONSTANTS_ALL)
             ->isNot('isPhp', true)
             ->back('first');
        $this->prepareQuery();

       // unwanted guests
        $this->atomFunctionIs(array_keys($positionsWithConstants))
             ->analyzerIsNot('self')
             ->savePropertyAs('fullnspath', 'fqn')
             ->outIs('ARGUMENT')
             ->isHash('rank', $positionsWithConstants, 'fqn')
             ->atomIs(array('Boolean', 'Null', 'Integer', 'Float', 'String', 'Concatenation', 'Logical', 'Bitoperation', 'Spaceship'))
             ->back('first');
        $this->prepareQuery();

        foreach($functions->alternative as $position => $functionsList) {
            $constantsWithPosition = array();
            foreach($functionsList as $function => $constants) {
                $fqn = makeFullNsPath($function);

                $constantsWithPosition[$fqn] = makeFullNsPath($constants, \FNP_CONSTANT);
            }

            $this->atomFunctionIs(array_keys($constantsWithPosition))
                 ->analyzerIsNot('self')
                 ->savePropertyAs('fullnspath', 'fqn')
                 ->outWithRank('ARGUMENT', $position)
                 ->is('constant', true)
                 ->atomIsNot(self::CONSTANTS_ALL)
                 ->back('first');
            $this->prepareQuery();

            $this->atomFunctionIs(array_keys($constantsWithPosition))
                 ->analyzerIsNot('self')
                 ->savePropertyAs('fullnspath', 'fqn')
                 ->outWithRank('ARGUMENT', $position)
                 ->atomIs(self::CONSTANTS_ALL)
                 ->isNot('isPhp', true)
                 ->back('first');
            $this->prepareQuery();

            $this->atomFunctionIs(array_keys($constantsWithPosition))
                 ->analyzerIsNot('self')
                 ->savePropertyAs('fullnspath', 'fnq')
                 ->outWithRank('ARGUMENT', $position)
                 ->atomIs(self::CONSTANTS_ALL)
                 ->is('isPhp', true)
                 ->isNotHash('fullnspath', $constantsWithPosition, 'fnq')
                 ->back('first');
            $this->prepareQuery();
        }

        /////////////////////////////////////////////////////////////////////////////
        // combinaison : several constants may be combined with a logical operator
        $positionsWithConstants = array();
        foreach($functions->combinaison as $position => $functionsList) {
            foreach((array) $functionsList as $function => $constants) {
                $fqn = makeFullNsPath($function);

                $positionsWithConstants[$fqn] = array((int) $position);
            }
        }

        // Not a PHP constant
        $this->atomFunctionIs(array_keys($positionsWithConstants))
             ->analyzerIsNot('self')
             ->savePropertyAs('fullnspath', 'fqn')
             ->outIs('ARGUMENT')
             ->isHash('rank', $positionsWithConstants, 'fqn')
             ->atomIs(self::CONSTANTS_ALL)
             ->isNot('isPhp', true)
             ->back('first');
        $this->prepareQuery();

        // in a logical combinaison, check that constants are at least PHP's one
        $this->atomFunctionIs(array_keys($positionsWithConstants))
             ->analyzerIsNot('self')
             ->savePropertyAs('fullnspath', 'fqn')
             ->outIs('ARGUMENT')
             ->isHash('rank', $positionsWithConstants, 'fqn')
             ->atomIs('Bitoperation')
             ->atomInsideNoDefinition(self::CONSTANTS_ALL)
             ->isNot('isPhp', true)
             ->back('first');
        $this->prepareQuery();

       // unwanted guests
       $this->atomFunctionIs(array_keys($positionsWithConstants))
            ->analyzerIsNot('self')
            ->savePropertyAs('fullnspath', 'fqn')
            ->outIs('ARGUMENT')
            ->isHash('rank', $positionsWithConstants, 'fqn')
            ->atomIs(array('Boolean', 'Null', 'Float'))
            ->back('first');
       $this->prepareQuery();

       $this->atomFunctionIs(array_keys($positionsWithConstants))
            ->analyzerIsNot('self')
            ->savePropertyAs('fullnspath', 'fqn')
            ->outWithRank('ARGUMENT', 0)
            ->isHash('rank', $positionsWithConstants, 'fqn')
            ->atomIs('Integer')
            ->codeIsNot(array('0', '-1'))
            ->back('first');
       $this->prepareQuery();

        // combinaison : several constants may be combined with a logical operator
        foreach($functions->combinaison as $position => $functionsList) {
            $position = (int) $position;

            $constantsWithPosition = array();
            foreach($functionsList as $function => $constants) {
                $fqn = makeFullNsPath($function);

                $constantsWithPosition[$fqn] = makeFullNsPath($constants, \FNP_CONSTANT);
            }

            // error_reporting(T_SEMICOLON)
            $this->atomFunctionIs(array_keys($constantsWithPosition))
                 ->analyzerIsNot('self')
                 ->savePropertyAs('fullnspath', 'fqn')
                 ->outWithRank('ARGUMENT', $position)
                 ->atomIs(array('Bitoperation', 'Identifier', 'Nsname'))
                 ->atomInsideNoDefinition(self::CONSTANTS_ALL)
                 ->isNot('isPhp', true)
                 ->back('first');
            $this->prepareQuery();

            $this->atomFunctionIs(array_keys($constantsWithPosition))
                 ->analyzerIsNot('self')
                 ->savePropertyAs('fullnspath', 'fqn')
                 ->outWithRank('ARGUMENT', $position)
                 ->atomIs(array('Bitoperation', 'Identifier', 'Nsname'))
                 ->atomInsideNoDefinition(self::CONSTANTS_ALL)
                 ->is('isPhp', true)
                 ->isNotHash('fullnspath', $constantsWithPosition, 'fqn')
                 ->back('first');
            $this->prepareQuery();

            // in a logical combinaison, check that constants are the one for the function
            $this->atomFunctionIs(array_keys($constantsWithPosition))
                 ->analyzerIsNot('self')
                 ->savePropertyAs('fullnspath', 'fqn')
                 ->outWithRank('ARGUMENT', $position)
                 ->atomIs(array('Bitoperation', 'Identifier', 'Nsname'))
                 ->atomInsideNoDefinition(self::CONSTANTS_ALL)
                 ->is('isPhp', true)
                 ->isNotHash('fullnspath', $constantsWithPosition, 'fqn')
                 ->back('first');
            $this->prepareQuery();
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Composer;

use Exakat\Analyzer\Analyzer;
use Exakat\Data\Composer;

class IsComposerInterface extends Analyzer {

    public function dependsOn(): array {
        return array('Interfaces/InterfaceUsage',
                    );
    }

    public function analyze(): void {
        $data = new Composer($this->config);

        $interfaces = $data->getComposerInterfaces();
        $interfacesFullNP = makeFullNsPath($interfaces);

        $this->atomIs('Class')
             ->outIs(array('IMPLEMENTS', 'EXTENDS'))
             ->fullnspathIs($interfacesFullNP);
        $this->prepareQuery();

        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->tokenIs(self::STATICCALL_TOKEN)
             ->codeIsNot('array')
             ->fullnspathIs($interfacesFullNP);
        $this->prepareQuery();

        $this->atomIs('Function')
             ->outIs('ARGUMENT')
             ->outIs('TYPEHINT')
             ->fullnspathIs($interfacesFullNP);
        $this->prepareQuery();

        $this->atomIs('Function')
             ->outIs('RETURNTYPE')
             ->fullnspathIs($interfacesFullNP);
        $this->prepareQuery();

        $this->atomIs('Usenamespace')
             ->outIs('USE')
             ->fullnspathIs($interfacesFullNP);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class InternalParameterType extends Analyzer {
    public function analyze(): void {
        $args = $this->methods->getInternalParameterType();

        $typeConversion = array('string'   => array('Magicconstant', 'Heredoc', 'String'),
                                'float'    => 'Float',
                                'int'      => 'Integer',
                                'numeric'  => array('Float', 'Integer'),
                                'resource' => '',
                                'bool'     => 'Boolean',
                                'array'    => 'Arrayliteral',
                                'void'     => 'Void',
                                'mixed'    => '' //explicitely here to avoid it
                                );

        foreach($args as $position => $types) {
            foreach($types as $type => $functions) {
                if (strpos($type, ',') !== false) {
                    continue; // No support for multiple type yet
                }

                if (!isset($typeConversion[$type]) || empty($typeConversion[$type])) {
                    continue;
                }

                $this->atomFunctionIs($functions)
                     ->raw('or( __.has("isPhp", true), __.has("isExt", true) )')
                     ->outWithRank('ARGUMENT', $position)

                     // only include literals (and closures and literal array)
                     ->atomIs(array('Integer', 'String', 'Arrayliteral', 'Float', 'Boolean', 'Null', 'Integer', 'Closure'), self::WITH_CONSTANTS)

                    // Constant (Identifier), logical, concatenation, addition ?
                    // Those will have to be replaced after more research

                    // All string equivalents
                     ->atomIsNot($typeConversion[$type])
                     ->back('first');
                $this->prepareQuery();
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
namespace Exakat\Analyzer\Traits;

use Exakat\Analyzer\Analyzer;

class UndefinedTrait extends Analyzer {
    public function analyze(): void {
        // class x { use t; } // no trait t {}
        $this->atomIs('Usetrait')
             ->outIs('USE')
             ->noTraitDefinition()
             ->isNotIgnored()
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class PrintfArguments extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        //The %2$s contains %1$04d monkeys
        //The %02s contains %-'.3d monkeys

        $countParameters = <<<REGEX
    d2 = it.get().value("fullcode").toString().findAll("(?<!%)%(?:\\\d+\\\\\\$)?[+-]?(?:[ 0-9\']*\\\.\\\d+)?(?:\\\d\\\d)?[bcdeEufFgGosxX]"); 
    d = [:];
    d2.each{
        x = it =~ "^%(\\\\d+)\\\\\\$"; 
        if (x.asBoolean() == true) {
            d[x[0][1]] = x[0][0];
        } else {
            d[d.size()] = it;
        }
    }
REGEX;

        // printf(' a %s %s', $a1, ...$a2);
        $this->atomFunctionIs(array('\\printf', '\\sprintf'))
             ->savePropertyAs('count', 'c')
             ->filter(
                $this->side()
                     ->outIs('ARGUMENT')
                     ->is('variadic', true)
             )
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->hasNoOut('CONCAT')
             // Count the number of ...variadic
             //(?:[ 0]|\'.{1})?-?\\\d*%(?:\\\.\\\d+)?
             ->raw(<<<GREMLIN
filter{
$countParameters
    c - 1 > d.size();
}
GREMLIN
)
             ->back('first');
        $this->prepareQuery();

        // printf(' a %s ', $a1, $a2);
        $this->atomFunctionIs(array('\\printf', '\\sprintf'))
             ->savePropertyAs('count', 'c')
             ->not(
                $this->side()
                     ->outIs('ARGUMENT')
                     ->is('variadic', true)
             )
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->hasNoOut('CONCAT')
             // Count the number of ...variadic
             //(?:[ 0]|\'.{1})?-?\\\d*%(?:\\\.\\\d+)?
             ->raw(<<<GREMLIN
filter{
$countParameters
    c - 1 != d.size();
}
GREMLIN
)
             ->back('first');
        $this->prepareQuery();

        // vsprintf(' a %s ',array( $a1, $a2));
        $this->atomFunctionIs('\\vsprintf')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->savePropertyAs('count', 'c')
             ->back('first')

             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->hasNoOut('CONCAT')
             //(?:[ 0]|\'.{1})?-?\\\d*%(?:\\\.\\\d+)?
             // + 0 is silly but actually works. :(
             ->raw(<<<GREMLIN
filter{
$countParameters
    c + 0 != d.size();
}
GREMLIN
)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class InvalidRegex extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        $functionList = makeFullNsPath(UnknownPregOption::$functions);

        $this->atomFunctionIs($functionList)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->hasNoOut('CONCAT')
             ->raw(<<<'GREMLIN'
map{
     if (it.get().value("delimiter") == "'") {
       regex = it.get().value('noDelimiter').replaceAll("\\\\(['\\\\])", "\$1");
     } else {
       regex = it.get().value('noDelimiter').replaceAll('\\\\([\$"\\\\])', "\$1");
     }
     
     [regex, it.get().value('fullcode')]
}

GREMLIN
);
        $regexSimple = $this->rawQuery();

        $this->atomFunctionIs($functionList)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(array('String', 'Concatenation'), self::WITH_CONSTANTS)
             ->hasOut('CONCAT')
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->outWithRank('ARGUMENT', 0)
                             ->atomIs(array('String', 'Identifier', 'Nsname', 'Staticconstant'))
                     )
             )
             ->not(
                $this->side()
                     ->outIs('CONCAT')
                     ->outIs('CONCAT')
             )
             ->raw(<<<'GREMLIN'
 where( 
    __.sideEffect{ c = it.get().value("count") - 1;}
      .out("CONCAT")
      .filter{ it.get().value("rank") == c;}
      .hasLabel("String", "Identifier", "Nsname", "Staticconstant")
      .not(where(__.out("CONCAT")))
)
.not( where( __.out("CONCAT").hasLabel("String", "Identifier", "Nsname", "Staticconstant").not(has("noDelimiter"))) )
.where( __.sideEffect{ liste = [];}
          .out("CONCAT").order().by('rank')
          .hasLabel("String", "Variable", "Array", "Functioncall", "Methodcall", "Staticmethodcall", "Member", "Staticproperty", "Identifier", "Nsname", "Staticconstant", 'Parenthesis')
          .sideEffect{ 
               if ('noDelimiter' in it.get().keys()) {
                   liste.add(it.get().value("noDelimiter").toString().replaceAll('\\\\([\$\\\'"\\\\])', "\$1"));
                } else {
                   liste.add("smi"); // smi is compatible with flags
                }
          }
          .fold()
)
.map{[liste.join(''), it.get().value('fullcode')]};
GREMLIN
);
        $regexComplex = $this->rawQuery();

        $regexList = array_merge($regexSimple->toArray(), $regexComplex->toArray());

        $invalid = array();
        foreach($regexList as list($regex, $fullcode)) {
            // @ is important here : we want preg_match to fail silently.
            if (false === @preg_match($regex, '')) {
                $invalid[] = $fullcode;
            }
        }

        if (empty($invalid)) {
            return;
        }

        $this->atomFunctionIs(UnknownPregOption::$functions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_ALL, self::WITH_CONSTANTS)
             ->fullcodeIs($invalid)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UndeclaredStaticProperty extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/OverwrittenProperties',
                    );
    }

    public function analyze(): void {
        // class a { public $a = 1;}
        // a::$a
        $this->atomIs('Staticproperty')
             ->inIs('DEFINITION')
             ->atomIs('Virtualproperty')
             ->hasOut('OVERWRITE')
             ->not(
                 $this->side()
                      ->outIs('OVERWRITE')
                      ->atomIs('Propertydefinition')
                      ->inIs('PPP')
                      ->is('static', true)
             )
             ->back('first');
        $this->prepareQuery();

        // class a { public $a = 1;}
        // a::$a
        $this->atomIs('Staticproperty')
             ->inIs('DEFINITION')
             ->atomIs('Propertydefinition')
             ->inIs('PPP')
             ->isNot('static', true)
             ->back('first');
        $this->prepareQuery();

        // class a { static public $a = 1;}
        // $a->$a
        $this->atomIs('Member')
             ->inIs('DEFINITION')
             ->atomIs('Propertydefinition')
             ->inIs('PPP')
             ->is('static', true)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Member')
             ->inIs('DEFINITION')
             ->atomIs('Virtualproperty')
             ->hasOut('OVERWRITE')
             ->not(
                 $this->side()
                      ->outIs('OVERWRITE')
                      ->atomIs('Propertydefinition')
                      ->inIs('PPP')
                      ->isNot('static', true)
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class CheckOnCallUsage extends Analyzer {
    public function dependsOn(): array {
        return array('Classes/IsNotFamily',
                    );
    }

    public function analyze(): void {
        // function __call($a, $b) { $this->$a(...$b); }
        $this->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs('__call')
             ->inIs('NAME')
             ->outIs('BLOCK')
             // no call to method_exists
             ->not(
                $this->side()
                     ->atomInsideNoDefinition('Functioncall')
                     ->functioncallIs('\\method_exists')
             )

            // call is made directly on $this
             ->outIs('EXPRESSION')
             ->atomIs('Methodcall')
             ->outIs('OBJECT')
             ->atomIs('This')
             ->back('first');
        $this->prepareQuery();

        // function __callStatic($a, $b) { self::$a(...$b); }
        $this->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs('__callstatic')
             ->inIs('NAME')
             ->outIs('BLOCK')
             // no call to method_exists
             ->not(
                $this->side()
                     ->atomInsideNoDefinition('Functioncall')
                     ->functioncallIs('\\method_exists')
             )

             ->outIs('EXPRESSION')
             ->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static', 'Parent'))
             ->analyzerIsNot('Classes/IsNotFamily')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class StripTagsSkipsClosedTag extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        // strip_tags($a, '<br />'); (br will not be ignored)
        $this->atomFunctionIs('\\strip_tags')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->regexIs('noDelimiter', '/>')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class PropagateCalls extends Complete {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                     'Complete/SetClassMethodRemoteDefinition',
                    );
    }

    public function analyze(): void {
        // No need to run twice
        $this->processLocalDefinition();
        $this->propagateGlobals();
        $this->propagateTypehint();
        $this->processFluentInterfaces();

        $count = $this->propagateCalls();

        $this->setCount($count);
    }

    private function propagateCalls($level = 0): int {
        $total = 0;

        $total += $this->processReturnedType();
        $total += $this->processParenthesis();

        if ($total > 0 && $level < 15) {
            $total += $this->propagateCalls($level + 1);
        }

        return $total;
    }

    private function processLocalDefinition(): int {
        //$a = new A; $a->method()
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs(array('Variabledefinition', 'Parametername', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              // Get only the first method found. Possibly OK for class, but may fail later
              ->filter(
                  $this->side()
                       ->atomIs('Class', self::WITHOUT_CONSTANTS)
                       ->goToAllParentsTraits(self::INCLUDE_SELF)
                       ->outIs('METHOD')
                       ->outIs('NAME')
                       ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
                       ->inIs('NAME')
                       ->range(0, 1)
                       ->as('origin')
                       ->dedup(array('first', 'origin'))
                       ->addETo('DEFINITION', 'first')
                )
              ->count();
        $c1 = $this->rawQuery()->toInt();

        //$a = new A; $a->property
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')
              ->atomIs(array('Variabledefinition', 'Parametername', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
       $c2 = $this->rawQuery()->toInt();

        //$a = function () {}; $a()
        $this->atomIs(array('Variabledefinition', 'Parametername', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
             ->outIs('DEFAULT')
             ->atomIs(array('Closure', 'Arrowfunction'), self::WITHOUT_CONSTANTS)
             ->hasIn('RIGHT')
             ->hasNoIn('DEFINITION')
             ->as('origin')
             ->back('first')
             ->outIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
             ->as('call')
             ->dedup(array('call', 'origin'))
             ->addEFrom('DEFINITION', 'origin')
             ->count();
       $c3 = $this->rawQuery()->toInt();

       return $c1 + $c2 + $c3;
    }

    private function processReturnedType(): int {
        // function foo() : X {}; $a = foo(); $a->b();
        $this->atomIs(self::FUNCTIONS_ALL, self::WITHOUT_CONSTANTS)
              ->hasOut('RETURNTYPE')
              ->outIs('DEFINITION')
              ->atomIs(self::FUNCTIONS_CALLS, self::WITHOUT_CONSTANTS)
              ->inIs('DEFAULT')
              ->atomIs(array('Propertydefinition', 'Variabledefinition', 'Globaldefinition', 'Staticdefinition'), self::WITHOUT_CONSTANTS)
              ->outIs('DEFINITION')
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->as('method')
              ->hasNoIn('DEFINITION')
              ->back('first')

              ->outIs('RETURNTYPE')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'method')
              ->count();
        $c1 = $this->rawQuery()->toInt();

        // function foo() : X {}; foo()->b();
        $this->atomIs(self::FUNCTIONS_ALL, self::WITHOUT_CONSTANTS)
              ->hasOut('RETURNTYPE')
              ->outIs('DEFINITION')
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->as('method')
              ->back('first')

              ->outIs('RETURNTYPE')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'method')
              ->count();
        $c1 += $this->rawQuery()->toInt();

        // function foo() : X {};foo()->b();
        $this->atomIs(self::FUNCTIONS_ALL, self::WITHOUT_CONSTANTS)
              ->hasOut('RETURNTYPE')
              ->outIs('DEFINITION')
              ->atomIs(self::FUNCTIONS_CALLS, self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->as('method')
              ->back('first')

              ->outIs('RETURNTYPE')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('method', 'origin'))
              ->addETo('DEFINITION', 'method')
              ->count();
        $c2 = $this->rawQuery()->toInt();

        $this->atomIs(self::FUNCTIONS_ALL, self::WITHOUT_CONSTANTS)
             ->hasOut('RETURNTYPE')
             ->outIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_CALLS, self::WITHOUT_CONSTANTS)
             ->inIs('DEFAULT')
             ->atomIs(array('Propertydefinition', 'Variabledefinition', 'Globaldefinition', 'Staticdefinition'), self::WITHOUT_CONSTANTS)
             ->outIs('DEFINITION')
             ->inIs('OBJECT')
             ->hasNoIn('DEFINITION')
             ->as('member')
             ->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->outIs('MEMBER')
             ->savePropertyAs('code', 'name')
             ->back('first')

             ->outIs('RETURNTYPE')
             ->inIs('DEFINITION')
             ->atomIs('Class', self::WITHOUT_CONSTANTS)
             ->goToAllParents(self::INCLUDE_SELF)
             ->outIs('PPP')
             ->outIs('PPP')
             ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('member', 'origin'))
             ->addETo('DEFINITION', 'member')
              ->count();
        $c3 = $this->rawQuery()->toInt();

       return $c1 + $c2 + $c3;
    }

    private function processParenthesis(): int {

        // (new x)->foo()
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->outIs('OBJECT')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c1 = $this->rawQuery()->toInt();

        // (new x)::foo()
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('OBJECT')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c2 = $this->rawQuery()->toInt();

        // (new x)::foo()
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c3 = $this->rawQuery()->toInt();

        // (new x)::foo()
        $this->atomIs('Staticproperty', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c4 = $this->rawQuery()->toInt();

        // (new x)::FOO
        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs('Parenthesis', self::WITHOUT_CONSTANTS)
              ->outIs('CODE')
              ->optional(
                $this->side()
                     ->atomIs('Assignation')
                     ->outIs('RIGHT')
              )
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c5 = $this->rawQuery()->toInt();

        return $c1 + $c2 + $c3 + $c4 + $c5;
    }

    private function propagateGlobals(): int {
        $this->atomIs('Virtualglobal', self::WITHOUT_CONSTANTS)
              ->outIs('DEFINITION')
              ->inIs('DEFINITION')
              ->atomIs('Variabledefinition')
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->as('theClass')

              ->back('first')
              ->outIs('DEFINITION')
              ->inIs('DEFINITION')
              ->atomIs('Variabledefinition')
              ->outIs('DEFINITION')
              ->inIs('OBJECT')
              ->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->as('theMethod')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')

              ->back('theClass')
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('theMethod', 'origin'))
              ->addETo('DEFINITION', 'theMethod')
              ->count();
        $c1 = $this->rawQuery()->toInt();

        $this->atomIs('Virtualglobal', self::WITHOUT_CONSTANTS)
              ->outIs('DEFINITION')
              ->inIs('DEFINITION')
              ->atomIs('Variabledefinition')
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->as('theClass')

              ->back('first')
              ->outIs('DEFINITION')
              ->inIs('DEFINITION')
              ->atomIs('Variabledefinition')
              ->outIs('DEFINITION')
              ->inIs('OBJECT')
              ->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->as('theProperty')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')

              ->back('theClass')
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'theProperty')
              ->count();
        $c2 = $this->rawQuery()->toInt();

        return $c1 + $c2;
    }

    private function propagateTypehint(): int {
        // class a { function m (); }
        // function foo( A $a) { $a->m(); }
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c1 = $this->rawQuery()->toInt();

        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->atomIs('Variableobject')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c2 = $this->rawQuery()->toInt();

        $this->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('CONSTANT')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('CONSTANT')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->atomIs('Parameter', self::WITHOUT_CONSTANTS)
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c3 = $this->rawQuery()->toInt();

        // Create link between static Class method and its definition
        // This works outside a class too, for static.
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs('Variable', self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->optional(
                  $this->side()
                       ->atomIs('Interface')
                       ->outIs('DEFINITION')
                       ->atomIs(self::STATIC_NAMES, self::WITHOUT_CONSTANTS)
                       ->inIs('IMPLEMENTS')
              )
              // No check on Atom == Class, as it may not exists
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->isNot('visibility', 'private')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))
              ->addETo('DEFINITION', 'first')
              ->count();
        $c4 = $this->rawQuery()->toInt();

        return $c1 + $c2 + $c3 + $c4;
    }

    private function processFluentInterfaces(): int {
        $this->atomIs(array('Methodcall', 'Staticmethodcall'), self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->back('first')

              ->outIs(array('OBJECT', 'CLASS'))
              ->atomIs(array('Methodcall', 'Staticmethodcall'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->atomIs(array('Method', 'Magicmethod'))
              ->inIs(array('METHOD', 'MAGICMETHOD'))

              ->atomIs(self::CLASSES_TRAITS, self::WITHOUT_CONSTANTS)
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('origin')
              ->dedup(array('first', 'origin'))

              ->addETo('DEFINITION', 'first')
              ->count();
        $res = $this->rawQuery();

        return $res->toInt();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class IsaMagicProperty extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/CreateMagicProperty',
                    );
    }

    public function analyze(): void {
        // echo $this->a;
        $this->atomIs('Member')
             ->is('isRead', true)
             ->inIs('DEFINITION')
             ->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs('__get', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // $this->a = 1;
        $this->atomIs('Member')
             ->is('isModified', true)
             ->inIs('DEFINITION')
             ->atomIs('Magicmethod')
             ->outIs('NAME')
             ->codeIs('__set', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class IsExtConstant extends Analyzer {
    public function dependsOn(): array {
        return array('Constants/ConstantUsage',
                    );
    }

    public function analyze(): void {
        // echo E_ALL
        $this->atomIs(self::STATIC_NAMES)
             ->analyzerIs('Constants/ConstantUsage')
             ->hasNoIn('DEFINITION')
             ->is('isPhp', true);
        $this->prepareQuery();

        // echo E_ALL
        $this->atomIs(self::STATIC_NAMES)
             ->analyzerIs('Constants/ConstantUsage')
             ->hasNoIn('DEFINITION')
             ->is('isExt', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class CustomConstantUsage extends Analyzer {
    public function dependsOn(): array {
        return array('Constants/ConstantUsage',
                    );
    }

    public function analyze(): void {
        $exts = $this->rulesets->listAllAnalyzer('Extensions');

        $constants = array();
        foreach($exts as $ext) {
            $inifile = str_replace('Extensions\Ext', '', $ext);
            $ini = $this->load($inifile, 'constants');

            if (!empty($ini[0])) {
                $constants[] = $ini;
            }
        }

        $constants = array_merge(...$constants);
        if (empty($constants)) {
            return;
        }

        $constants = makeFullNsPath($constants);

        // @note NSnamed are OK by default (may be not always!)
        $this->atomIs(self::CONSTANTS_ALL)
             ->analyzerIs('Constants/ConstantUsage')
             ->fullnspathIsNot($constants)
             ->inIs('DEFINITION')
             ->atomIs(array('Constant', 'Defineconstant'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class DefinedConstants extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/IsStubStructure',
                     'Complete/IsPhpStructure',
                     'Complete/MakeClassConstantDefinition',
                     'Complete/OverwrittenConstants',
                     'Classes/IsExtClass',
                    );
    }

    public function analyze(): void {
        // constants defined at the class level
        // constants defined at the parents level
        // This includes interfaces
        $this->atomIs('Staticconstant')
             ->inIs('DEFINITION')
             ->atomIs('Constant')
             ->back('first');
        $this->prepareQuery();

        // constants defined in a class of an extension
        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->is('isPhp', true)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->is('isStub', true)
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->is('isExt', true)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class MakeClassMethodDefinition extends Complete {
    public function dependsOn(): array {
        return array('Complete/SetParentDefinition',
                     'Complete/CreateDefaultValues',
                     'Complete/OverwrittenMethods',
                    );
    }

    public function analyze(): void {
        // Warning : no support for overwritten methods : ALL methods are linked

        // Create link between static Class method and its definition
        // This works outside a class too, for static.
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_TRAITS, self::WITHOUT_CONSTANTS)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs('Static', self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_TRAITS,  self::WITHOUT_CONSTANTS)
              ->goToAllChildren(self::EXCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between Class method and definition
        // This works only for $this
        // First case for the local class
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
             ->outIs('OBJECT')
             ->atomIs('This', self::WITHOUT_CONSTANTS)
             ->back('first')
             ->inIs('DEFINITION')
             ->outIs('OVERWRITE')
             ->atomIs(self::FUNCTIONS_METHOD)
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Second case for the local traits
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToClass(self::CLASSES_TRAITS)
              ->outIs('USE')
              ->outIs('USE')
              ->inIs('DEFINITION')
              ->atomIs('Trait')

              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Third case for the parents
        // class only
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToClass(self::CLASSES_TRAITS)
              ->goToAllParents(self::EXCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // class's traits
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToClass(self::CLASSES_TRAITS)
              ->goToAllParents(self::EXCLUDE_SELF)
              ->outIs('USE')
              ->hasNoOut('BLOCK')
              ->outIs('USE')
              ->inIs('DEFINITION')
              ->atomIs('Trait')
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // This will take care of the first step : class - trait - parent. (Above is currently not detailled, any method is linked.)


        // Create link between Class method and definition
        // This works only for $this
        // use a { a as d }
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->hasNoIn('DEFINITION')
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToInstruction(self::CLASSES_TRAITS)
              ->goToAllParents(self::INCLUDE_SELF)

              ->outIs('USE')
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs('As', self::WITHOUT_CONSTANTS)
              ->outIs('AS')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('AS')
              ->outIs('NAME')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'realname')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->atomIs('Trait', self::WITHOUT_CONSTANTS)
              ->GoToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'realname', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // use a,b { a::d insteadof b }
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToInstruction(self::CLASSES_TRAITS)
              ->goToAllParents(self::INCLUDE_SELF)

              ->outIs('USE')
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs('Insteadof', self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->outIs('METHOD')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->atomIs('Trait', self::WITHOUT_CONSTANTS)
              ->GoToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between Class method and definition
        // This works only for $this
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToInstruction(self::CLASSES_TRAITS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->as('theClass')

              ->outIs('USE')
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs('As', self::WITHOUT_CONSTANTS)
              ->outIs('AS')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('AS')
              ->outIs('NAME')
              ->savePropertyAs('lccode', 'realname')
              ->back('theClass')

              ->outIs('USE')
              ->outIs('USE')
              ->inIs('DEFINITION')
              ->atomIs('Trait', self::WITHOUT_CONSTANTS)
              ->GoToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'realname', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between Class method and definition
        // class x { function foo() { $this->b(); }}
        // class y extends x { function b() {  }} // class y has no class FOO
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('OBJECT')
              ->atomIs('This', self::WITHOUT_CONSTANTS)
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->goToFunction()
              ->outIs('NAME')
              ->savePropertyAs('lccode', 'methodname')

              ->goToInstruction(self::CLASSES_TRAITS)
              ->goToAllChildren(self::EXCLUDE_SELF)
              ->as('theClass')

              ->not(
                $this->side()
                     ->outIs(array('METHOD', 'MAGICMETHOD'))
                     ->outIs('NAME')
                     ->samePropertyAs('lccode', 'methodname', self::CASE_INSENSITIVE)
              )

              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->not(
                $this->side()
                     ->outIs('DEFINITION')
                     ->raw('where(eq("first"))')
              )
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between Class method and definition
        // This works only for $this
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->goToAllParents(self::INCLUDE_SELF)

              ->outIs('USE')
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs(array('As', 'Insteadof'), self::WITHOUT_CONSTANTS)
              ->outIs(array('AS', 'INSTEADOF'))
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs(array('AS', 'INSTEADOF'))
              ->outIs('NAME')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'realname')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->atomIs('Trait', self::WITHOUT_CONSTANTS)
              ->GoToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'realname', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between Class method and definition
        // This works only for $this
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->goToAllParents(self::INCLUDE_SELF)
              ->as('theClass')

              ->outIs('USE')
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs('As', self::WITHOUT_CONSTANTS)
              ->outIs('AS')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('AS')
              ->outIs('NAME')
              ->savePropertyAs('lccode', 'realname')
              ->back('theClass')

              ->outIs('USE')
              ->outIs('USE')
              ->inIs('DEFINITION')
              ->atomIs('Trait', self::WITHOUT_CONSTANTS)
              ->GoToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))

              ->outIs('NAME')
              ->samePropertyAs('lccode', 'realname', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between static Class method and its definition
        // This works outside a class too, for static.
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs(array('Identifier', 'Nsname', 'Self', 'Static'), self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::EXCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->isNot('visibility', 'private')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between static Class method and its definition
        // This works outside a class too, for static.
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::FUNCTIONS_METHOD)
             )
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->atomIs('Parent', self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->GoToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->isNot('visibility', 'private')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Create link between constructor and new call
        $this->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->atomIs(array('Newcall', 'Newcallname', 'Self', 'Parent'), self::WITHOUT_CONSTANTS)
              ->has('fullnspath')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->outIs('MAGICMETHOD')
              ->outIs('NAME')
              ->codeIs('__construct', self::TRANSLATE, self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        $this->atomIs('New', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('NEW')
              ->atomIs(array('Newcall', 'Newcallname', 'Self', 'Parent'), self::WITHOUT_CONSTANTS)
              ->has('fullnspath')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->savePropertyAs('lccode', 'name')
              ->inIs('NAME')
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        $this->atomIs('New', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('NEW')
              ->atomIs(array('Newcall', 'Newcallname', 'Self', 'Parent'), self::WITHOUT_CONSTANTS)
              ->has('fullnspath')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->outIs('EXTENDS')
              ->inIs('DEFINITION')
              ->raw(<<<'GREMLIN'
until( __.out("MAGICMETHOD").out("NAME").has("fullcode", "__construct")).repeat( __.out("EXTENDS").in("DEFINITION"))
GREMLIN
)
              ->outIs('MAGICMETHOD')
              ->codeIs('__construct', self::TRANSLATE, self::CASE_INSENSITIVE)
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Cases of the attributes (simple case, with no inheritence)
        $this->atomIs(self::ATTRIBUTE_ATOMS, self::WITHOUT_CONSTANTS)
             ->outIs('ATTRIBUTE')
             ->as('attribute')
             ->inIs('DEFINITION')
             ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
             ->outIs('MAGICMETHOD')
             ->codeIs('__construct', self::TRANSLATE, self::CASE_INSENSITIVE)
             ->addETo('DEFINITION', 'attribute');
        $this->prepareQuery();

        // Create link between __clone and clone
        // parenthesis, typehint, local new,
        $this->atomIs('Clone', self::WITHOUT_CONSTANTS)
              ->outIs('CLONE')
              ->inIs('DEFINITION')
              ->inIs('NAME')
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_TRAITS, self::WITHOUT_CONSTANTS)
              ->outIs('MAGICMETHOD')
              ->codeIs('__clone', self::TRANSLATE, self::CASE_INSENSITIVE)
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // x $a; $a::Constante -> class x { const Constante}
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->outIs('CLASS')
              ->atomIs(array('Variable', 'Member', 'Staticproperty'))
              ->goToTypehint()
              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Classanonymous', 'Interface'), self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->atomIs(array('Method', 'Magicmethod'), self::WITHOUT_CONSTANTS)
              ->isNot('visibility', 'private')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class ConstRecommended extends Analyzer {
    public function dependsOn(): array {
        return array('Constants/ConstantUsage',
                    );
    }

    public function analyze(): void {
        // define('const', literal);
        // define('const', other constant);
        // define('const', expression);
        $this->atomIs('Defineconstant')
//             ->atomIsNot(array('Identifier', 'Nsname','String', 'Float', 'Integer', 'Boolean', 'Null', 'Staticconstant', 'Variable'))
             ->noAtomInside(array('Variable', 'Member', 'Staticproperty', 'Functioncall', 'Staticmethodcall', 'Methodcall'));
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UndefinedProperty extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/CreateMagicProperty',
                     'Classes/HasMagicProperty',
                     'Complete/SetClassPropertyDefinitionWithTypehint',
                     'Complete/IsPhpStructure',
                     'Complete/IsStubStructure',
                     'Classes/ExtendsStdclass',
                    );
    }

    public function analyze(): void {
        // Check proeprties with local definition
        $this->atomIs(self::PROPERTIES)

            // do not have __get/__set set up (trait or parents or else)
            ->not(
                $this->side()
                     ->goToClass()
                     ->analyzerIs('Classes/HasMagicProperty')
            )

            // Do not extends stdclass
            ->not(
                $this->side()
                     ->goToClass()
                     ->analyzerIs('Classes/ExtendsStdclass')
            )

            // Do not use  #[allowdynamicproperties ]
            ->hasNotAttribute('\\allowdynamicproperties')

             // not a property in a parent class in the code
             ->inIs('DEFINITION')
             ->atomIs('Virtualproperty')
             ->not(
                $this->side()
                     ->outIs('OVERWRITE')
                     ->atomIs('Propertydefinition')
                     ->inIs('PPP')
                     ->isNot('visibility','private')
             )

             // Not a property in a included component
             ->back('first');
        $this->prepareQuery();

        // Check on properties with external definition
        // relies on isPHP, isStub, isExt
        $this->atomIs(self::PROPERTIES)
             ->analyzerIsNot('self')
             ->hasNoIn('DEFINITION') // No definition found

             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true)

             ->not( // No dynamic elements
                $this->side()
                     ->atomIs('Member')
                     ->outIs('MEMBER')
                     ->tokenIsNot('T_STRING')
             )
             ->not( // No dynamic elements
                $this->side()
                     ->atomIs('Staticproperty')
                     ->outIs('MEMBER')
                     ->tokenIsNot('T_VARIABLE')
             )

             // Not a property in a included component
             ->back('first');
        $this->prepareQuery();

        // case of public access
        $this->atomIs(self::PROPERTIES)
             ->analyzerIsNot('self')
             ->hasNoIn('DEFINITION')
             ->outIs(array('OBJECT', 'CLASS'))
             ->inIs('DEFINITION')
             ->inIsIE('NAME')
             ->outIs('TYPEHINT')
             ->inIs('DEFINITION')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Interfaces;

use Exakat\Analyzer\Analyzer;

class UndefinedInterfaces extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/VariableTypehint',
                    );
    }

    public function analyze(): void {
        // interface used in a class
        $this->atomIs(self::CLASSES_ALL)
             ->outIs('IMPLEMENTS')
             ->hasNoIn('DEFINITION')
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true);
        $this->prepareQuery();

        // interface extending another interface
        $this->atomIs('Interface')
             ->outIs('EXTENDS')
             ->hasNoIn('DEFINITION')
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true);
        $this->prepareQuery();

        // interface used in a instanceof nor a Typehint but not defined
        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->atomIsNot(array('Self', 'Parent'))
             ->has('fullnspath')
             ->noClassDefinition()
             ->noInterfaceDefinition()
             ->isNotIgnored()
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true);
        $this->prepareQuery();

        // types, (typeints or returntype)
        $this->atomIs(self::CONSTANTS_ALL)
             ->hasIn(self::TYPE_LINKS)
             ->atomIsNot(array('Self', 'Parent'))
             ->has('fullnspath')
             ->noClassDefinition()
             ->noInterfaceDefinition()
             ->noUseDefinition()
             ->has('line')
             ->isNotIgnored()
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassRemoteDefinitionWithLocalNew extends Complete {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        // $a = new X(); $a->m1();
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')  // No check on atoms :
              ->atomIs(array('Variabledefinition', 'Parametername', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // $a = new X(); $a->p;
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')
              ->atomIs(array('Variabledefinition', 'Parametername', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->atomIs('New', self::WITHOUT_CONSTANTS)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->atomIs('Propertydefinition')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassRemoteDefinitionWithReturnTypehint extends Complete {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        // class a { function b() }; function foo() : a {}; $a = foo(); $a->b()
        // class a { function b() }; function foo() : a {}; foo()->b()
        $this->atomIs(self::FUNCTIONS_ALL, self::WITHOUT_CONSTANTS)
              ->hasOut('RETURNTYPE')
              ->outIs('DEFINITION')
              ->atomIs(self::FUNCTIONS_CALLS, self::WITHOUT_CONSTANTS)
              ->optional(
                $this->side()
                     ->inIs('DEFAULT')
                     ->atomIs(array('Propertydefinition', 'Variabledefinition', 'Globaldefinition', 'Staticdefinition'), self::WITHOUT_CONSTANTS)
                     ->outIs('DEFINITION')
              )
              ->inIs('OBJECT')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->inIs('METHOD')
              ->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->hasNoIn('DEFINITION')
              ->as('method')
              ->back('first')

              ->outIs('RETURNTYPE')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'method');
        $this->prepareQuery();

        // class a { private $b }; function foo() : a {}; $a = foo(); $a->b
        // class a { private $b }; function foo() : a {}; foo()->b
        $this->atomIs(self::FUNCTIONS_ALL, self::WITHOUT_CONSTANTS)
             ->hasOut('RETURNTYPE')
             ->outIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_CALLS, self::WITHOUT_CONSTANTS)
              ->optional(
                $this->side()
                     ->inIs('DEFAULT')
                     ->atomIs(array('Propertydefinition', 'Variabledefinition', 'Globaldefinition', 'Staticdefinition'), self::WITHOUT_CONSTANTS)
                     ->outIs('DEFINITION')
              )

             ->inIs('OBJECT')
             ->hasNoIn('DEFINITION')
             ->as('member')
             ->atomIs('Member', self::WITHOUT_CONSTANTS)
             ->outIs('MEMBER')
             ->savePropertyAs('code', 'name')
             ->back('first')

             ->outIs('RETURNTYPE')
             ->inIs('DEFINITION')
             ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
             ->goToAllParents(self::INCLUDE_SELF)
             ->outIs('PPP')
             ->outIs('PPP')
             ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
             ->addETo('DEFINITION', 'member');
        $this->prepareQuery();

        // $b = foo(); $b->p; function foo() : C {}
        $this->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('member')
              ->hasNoIn('DEFINITION')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->inIs('MEMBER')
              ->outIs('OBJECT')
              ->inIs('DEFINITION')
              ->atomIs(array('Variabledefinition', 'Parametername', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'Virtualproperty'), self::WITHOUT_CONSTANTS)
              ->outIs('DEFAULT')
              ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->outIs('RETURNTYPE')
              ->inIs('DEFINITION')
              ->atomIs(self::CLASSES_ALL, self::WITHOUT_CONSTANTS)
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'member');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UndefinedStaticclass extends Analyzer {
    public function analyze(): void {
        //echo  undefinedClass::class
        $this->atomIs('Staticclass')
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->outIs('CLASS')
             ->hasNoIn('DEFINITION')
             ->atomIs(self::STATIC_NAMES)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);

/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Type;

use Exakat\Analyzer\Analyzer;

class Pack extends Analyzer {
    protected $analyzerName = 'Pack';

    public function dependsOn(): array {
        return array('Variables/IsLocalConstant',
                     'Complete/PropagateConstants',
                     'Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        $packFunctions = array('\\pack',
                               '\\unpack',
                               );

        // pack("nvc*", 0x1234, 0x5678, 65, 66);
        $this->atomFunctionIs($packFunctions)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ImplodeArgsOrder extends Analyzer {
    public function dependsOn(): array {
        return array('Variables/IsLocalConstant',
                     'Complete/CreateDefaultValues',
                     'Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        $functions = array('\\join', '\\implode');

        // detect an array in first arg
        // Constants
        $this->atomFunctionIs($functions)
             ->analyzerIsNot('self')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();

        // Local variable, argument 0
        $this->atomFunctionIs($functions)
             ->analyzerIsNot('self')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->inIsIE('NAME')
             ->outIs('DEFAULT')
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();

        // Returntype
        $this->atomFunctionIs($functions)
             ->analyzerIsNot('self')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::FUNCTIONS_CALLS)
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')
             ->fullnspathIs('\\array')
             ->back('first');
        $this->prepareQuery();

        // detect a string in second arg
        $this->atomFunctionIs($functions)
             ->analyzerIsNot('self')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();

        // Local variable, argument 1
        $this->atomFunctionIs($functions)
             ->analyzerIsNot('self')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->inIsIE('NAME')
             ->outIs('DEFAULT')
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();

        // Returntype
        $this->atomFunctionIs($functions)
             ->analyzerIsNot('self')
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(self::FUNCTIONS_CALLS)
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')
             ->fullnspathIs('\\string')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class NoLiteralForReference extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/SetClassMethodRemoteDefinition',
                     'Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        $atoms = array('Integer',
                       'Null',
                       'Void',
                       'Float',
                       'Addition',
                       'Multiplication',
                       'Bitshift',
                       'Logical',
                       'Ternary',
                       'Identifier',
                       'Nsname',
                       'Assignation',
                       'Ternary',
                       );

        // foo(1)
        // function foo(&$r) {}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->is('reference', true)
             ->goToParameterUsage()
             ->is('constant', true)
             ->atomIsNot(array('Void', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
             ->inIs('ARGUMENT');
        $this->prepareQuery();

        // foo(bar_without_reference)
        // function foo(&$r) {}
        $this->atomIs(self::CALLS)
             ->outIs('ARGUMENT')
             ->as('argument')

             ->atomIs(array('Functioncall', 'Methodcall', 'Staticmethodcall'))
             ->inIs('DEFINITION')
             ->isNot('reference', true)

             ->back('argument')
             ->goToParameterDefinition()
             ->is('reference', true)
             ->back('first');
        $this->prepareQuery();

        // function &foo($r) { return 2; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->is('reference', true)
             ->outIs('RETURNED')
             ->hasIn('RETURN')
             ->outIsIE('CODE') // Skip parenthesis
             ->atomIs($atoms)
             ->back('first');
        $this->prepareQuery();

        // function &foo($r) { return foo_without_ref(); }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->is('reference', true)
             ->outIs('RETURNED')
             ->hasIn('RETURN')
             ->outIsIE('CODE') // Skip parenthesis
             ->atomIs(self::CALLS)
             ->inIs('DEFINITION')
             ->isNot('reference', true)
             ->outIs('RETURNTYPE')
             ->atomIs(array('Scalartypehint', 'Void'))
             ->back('first');
        $this->prepareQuery();

        // Does PHP return references too ? Shall we cover native PHP functions?
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class FollowClosureDefinition extends Complete {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        // immediate usage : in parenthesis (function () {})()
        $this->atomIs(array('Closure', 'Arrowfunction'), self::WITHOUT_CONSTANTS)
             ->inIsIE('RIGHT') // Skip all $closure =
             ->inIs('CODE')
             ->atomIs('Parenthesis')
             ->inIs('NAME')
             ->atomIs('Functioncall')
             ->hasNoIn('DEFINITION')
             ->addEFrom('DEFINITION', 'first');
        $this->prepareQuery();

        // local usage
        $this->atomIs(array('Closure', 'Arrowfunction'), self::WITHOUT_CONSTANTS)
             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->inIs('DEFINITION')  // Find all variable usage
             ->outIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->addEFrom('DEFINITION', 'first');
        $this->prepareQuery();

        // relayed usage foo(function(){}); function foo($a) { $a();}
        // relayed usage $d = function(){}; foo($d); function foo($a) { $a();}
        $this->atomIs('Functioncall')
             ->outIs('NAME')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Parameter')
             ->goToParameterUsage()
             ->atomIs(array('Closure', 'Arrowfunction'), self::WITH_VARIABLES)
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // First class Callable
        // immediate usage
        $this->atomIs('Callable', self::WITHOUT_CONSTANTS)
             ->tokenIs(array('T_STRING', 'T_NS_SEPARATOR'))

             ->inIs('DEFINITION')
             ->as('definition')
             ->back('first')

             ->inIs('NAME')
             ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->addEFrom('DEFINITION', 'definition');
        $this->prepareQuery();

        // local usage
        $this->atomIs('Callable', self::WITHOUT_CONSTANTS)
             ->tokenIs(array('T_STRING', 'T_NS_SEPARATOR'))

             ->inIs('DEFINITION')
             ->as('definition')
             ->back('first')

             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->inIs('DEFINITION')  // Find all variable usage
             ->outIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->addEFrom('DEFINITION', 'definition');
        $this->prepareQuery();

        // On a method $o->b()
        $this->atomIs('Callable', self::WITHOUT_CONSTANTS)
             ->tokenIs('T_OBJECT_OPERATOR') // T_NULLSAFE_OBJECT_OPERATOR is not possible

             ->outIs('METHOD')
             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'method')
             ->back('first')

             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->outIs('DEFAULT')
             ->atomIs(array('New'))
             ->outIs('NEW')
             ->inIs('DEFINITION')
             ->atomIs('Class')
             ->outIs('METHOD')
             ->as('definition')
             ->outIs('NAME')
             ->samePropertyAs('fullcode', 'method')
             ->back('first')

             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->inIs('DEFINITION')  // Find all variable usage
             ->outIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->addEFrom('DEFINITION', 'definition');
        $this->prepareQuery();

        // On a static method A::b()
        $this->atomIs('Callable', self::WITHOUT_CONSTANTS)
             ->tokenIs('T_DOUBLE_COLON')

             ->outIs('METHOD')
             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'method')
             ->back('first')

             ->outIs('CLASS')
             ->inIs('DEFINITION')
             ->atomIs('Class')
             ->outIs('METHOD')
             ->as('definition')
             ->outIs('NAME')
             ->samePropertyAs('fullcode', 'method')
             ->back('first')

             ->inIs('RIGHT')
             ->outIs('LEFT')
             ->inIs('DEFINITION')  // Find all variable usage
             ->outIs('DEFINITION')
             ->inIs('NAME')
             ->atomIs('Functioncall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->addEFrom('DEFINITION', 'definition');
        $this->prepareQuery();

        // more complex structures are missing ;
        // argument, properties, etc.
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;


class CouldBeNull extends CouldBeType {
    public function analyze(): void {
        $nullAtoms = array('Null');

        // property : based on default value (created or not)
        $this->checkPropertyDefault($nullAtoms);

        $this->checkPropertyRelayedDefault($nullAtoms);

        // property relayed typehint
        $this->checkPropertyRelayedTypehint(array('Scalartypehint'), array('\\null'));

        // property relayed typehint
        $this->checkPropertyWithCalls(array('Scalartypehint'), array('\\null'));
        $this->checkPropertyWithPHPCalls('null');

        // argument type : $x = $arg ?? 2;
        $this->atomIs('Propertydefinition')
             ->analyzerIsNot('self')
             ->outIs('DEFINITION')
             ->inIs('LEFT')
             ->atomIs(array('Coalesce'))
             ->back('first');
        $this->prepareQuery();

        // argument type : $this->x ??= 2;
        $this->atomIs('Propertydefinition')
             ->analyzerIsNot('self')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Assignation'))
             ->codeIs(array('??='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // return type
        $this->checkReturnedAtoms($nullAtoms);

        $this->checkReturnedCalls(array('Scalartypehint'), array('\\null'));

        $this->checkReturnedPHPTypes('null');

        $this->checkReturnedDefault($nullAtoms);

        $this->checkReturnedTypehint(array('Scalartypehint'), array('\\null'));

        // arguments
        // function ($a = int)
        $this->checkArgumentDefaultValues($nullAtoms);

        // function ($a) { bar($a);} function bar(array $b) {}
        $this->checkRelayedArgument(array('Scalartypehint'), array('\\null'));

        // function ($a) { pow($a, 2);}
        $this->checkRelayedArgumentToPHP('null');

        // argument validation
        $this->checkArgumentValidation(array('\\is_null'), $nullAtoms);

        // argument because used in a specific operation
        // $arg && ''
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Coalesce'))
             ->back('result');
        $this->prepareQuery();

        // $arg === null
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Comparison'))
             ->outIs(array('LEFT', 'RIGHT'))
             ->atomIs('Null')
             ->back('result');
        $this->prepareQuery();

        // May also cover if( $arg).,
        // May also cover coalesce, ternary.
        // short assignations
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;

use Exakat\Query\DSL\FollowParAs;

class CouldBeString extends CouldBeType {

    public function analyze(): void {
        $stringAtoms = self::STRINGS_LITERALS;
        $stringFnp = array('\\string');

        $this->checkPropertyDefault($stringAtoms);

        // property relayed default
        $this->checkPropertyRelayedDefault($stringAtoms);

        // property relayed typehint
        $this->checkPropertyRelayedTypehint(array('Scalartypehint'), $stringFnp);

        // property relayed typehint
        $this->checkPropertyWithCalls(array('Scalartypehint'), $stringFnp);
        $this->checkPropertyWithPHPCalls('string');

        // $a[$b->property] : could be a string or an integer
        $this->atomIs('Propertydefinition')
             ->analyzerIsNot('self')
             ->outIs('DEFAULT')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs('INDEX')
             ->atomIs('Array')
             ->back('first');
        $this->prepareQuery();

        // return type
        $this->checkReturnedAtoms($stringAtoms);

        $this->checkReturnedCalls(array('Scalartypehint'), $stringFnp);

        $this->checkReturnedPHPTypes('string');

        $this->checkReturnedDefault($stringAtoms);

        $this->checkReturnedTypehint(array('Scalartypehint'), $stringFnp);

        // class a implements b { function () : string {} }
        $this->checkOverwrittenReturnType($stringFnp);

        // return type : return $a->b .= "s";
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('self')
             ->outIs('RETURNED')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->analyzerIsNot('self')
             ->atomIs('Assignation')
             ->codeIs(array('.='))
             ->back('first');
        $this->prepareQuery();

        // argument type

        // class a implements b { function ($a = string) {} }
        $this->checkOverwrittenArgumentType($stringFnp);

        // function ($a = array())
        $this->checkArgumentDefaultValues($stringAtoms);

        // function ($a) { bar($a);} function bar(array $b) {}
        $this->checkRelayedArgument(array('Scalartypehint'), $stringFnp);

        // function ($a) { array_diff($a);}
        $this->checkRelayedArgumentToPHP('string');

        // is_string
        $this->checkArgumentValidation(array('\\is_string'), $stringAtoms);

        // (string) or strval
        $this->checkCastArgument('T_STRING_CAST', array('\\strval'));

        // foo('a'); function foo($a) {}
        $this->checkCallingArgumentType(array('String', 'Heredoc', 'Concatenation'));

        // argument because used in a specific operation
        // $arg . ''
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->hasIn('CONCAT')
             ->back('result');
        $this->prepareQuery();

        // $a[$arg] : could be a string or an integer
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs('INDEX')
             ->atomIs('Array')
             ->back('result');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;

use Exakat\Query\DSL\FollowParAs;

class CouldBeFloat extends CouldBeType {
    public function analyze(): void {
        $floatAtoms = array('Float', 'Addition', 'Multiplication', 'Not', 'Power');
        $floatFnp = array('\\float');

        // property : based on default value (created or not)
        $this->checkPropertyDefault($floatAtoms);

        // property : based usage of the property
        $this->checkPropertyUsage($floatAtoms);

        $this->checkPropertyRelayedDefault($floatAtoms);

        // property relayed typehint
        $this->checkPropertyRelayedTypehint(array('Scalartypehint'), $floatFnp);

        // property relayed typehint
        $this->checkPropertyWithCalls(array('Scalartypehint'), $floatFnp);
        $this->checkPropertyWithPHPCalls('float');

        // argument type : $x[$arg]
        $this->atomIs('Propertydefinition')
             ->analyzerIsNot('self')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Assignation'))
             ->codeIs(array('+=', '-=', '*=', '%=', '/=', '**='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // return type
        $this->checkReturnedAtoms($floatAtoms);

        $this->checkReturnedCalls(array('Scalartypehint'), $floatFnp);

        $this->checkReturnedPHPTypes('float');

        $this->checkReturnedDefault($floatAtoms);

        $this->checkReturnedTypehint(array('Scalartypehint'), $floatFnp);

        // class a implements b { function () : float {} }
        $this->checkOverwrittenReturnType($floatFnp);

        // return type : return $a->b += 3.3;
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('self')
             ->outIs('RETURNED')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->analyzerIsNot('self')
             ->atomIs('Assignation')
             ->codeIs(array('+=', '-=', '*=', '**=', '/=', '%=', '<<=', '>>=', '&=', '|=', '^='))
             ->back('first');
        $this->prepareQuery();

        // argument type : $x[$arg]
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Assignation'))
             ->codeIs(array('+=', '-=', '*=', '%=', '/=', '**='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->back('result');
        $this->prepareQuery();

        // class a implements b { function (int $a) {} }
        $this->checkOverwrittenArgumentType($floatFnp);

        // function ($a = float)
        $this->checkArgumentDefaultValues($floatAtoms);

        // function ($a) { bar($a);} function bar(float $b) {}
        $this->checkRelayedArgument(array('Scalartypehint'), $floatFnp);

        // function ($a) { pow($a, 2);}
        $this->checkRelayedArgumentToPHP('float');

        // argument validation
        $this->checkArgumentValidation(array('\\is_float'), $floatAtoms);

        // foo(1); function foo($a) {}
        $this->checkCallingArgumentType(array('Float'));

        // argument because used in a specific operation
        // $arg && ''
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Addition', 'Power', 'Multiplication'))
             ->back('result');
        $this->prepareQuery();

        // May also cover if( $arg).,
        // May also cover coalesce, ternary.
        // short assignations
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;

use Exakat\Query\DSL\FollowParAs;

class CouldBeInt extends CouldBeType {
    public function analyze(): void {
        $intAtoms = array('Integer', 'Addition', 'Multiplication', 'Not', 'Power', 'Preplusplus', 'Postplusplus', 'Spaceship');
        $intFnp = array('\\int');

        // property : based on default value (created or not)
        $this->checkPropertyDefault($intAtoms);

        // property : based usage of the property
        $this->checkPropertyUsage($intAtoms);

        $this->checkPropertyRelayedDefault($intAtoms);

        // property relayed typehint
        $this->checkPropertyRelayedTypehint(array('Scalartypehint'), $intFnp);

        // property relayed typehint
        $this->checkPropertyWithCalls(array('Scalartypehint'), $intFnp);
        $this->checkPropertyWithPHPCalls('int');

        // argument type : $x[$arg]
        $this->atomIs('Propertydefinition')
             ->analyzerIsNot('self')
             ->outIs('DEFINITION')
             ->inIs('INDEX')
             ->atomIs(array('Array'))
             ->back('first');
        $this->prepareQuery();

        // argument type : $x[$arg]
        $this->atomIs('Propertydefinition')
             ->analyzerIsNot('self')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Assignation'))
             ->codeIs(array('+=', '-=', '*=', '%=', '/=', '**='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        // return type
        $this->checkReturnedAtoms($intAtoms);

        $this->checkReturnedCalls(array('Scalartypehint'), $intFnp);

        $this->checkReturnedPHPTypes('int');

        $this->checkReturnedDefault($intAtoms);

        $this->checkReturnedTypehint(array('Scalartypehint'), $intFnp);

        // class a implements b { function () : int {} }
        $this->checkOverwrittenReturnType($intFnp);

        // return type : return $a->b += 3;
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('self')
             ->outIs('RETURNED')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->analyzerIsNot('self')
             ->atomIs('Assignation')
             ->codeIs(array('+=', '-=', '*=', '**=', '/=', '%=', '<<=', '>>=', '&=', '|=', '^='))
             ->back('first');
        $this->prepareQuery();

        // argument type : $x[$arg]
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs('INDEX')
             ->atomIs(array('Array'))
             ->back('result');
        $this->prepareQuery();

        // argument type : $x[$arg]
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Assignation'))
             ->codeIs(array('+=', '-=', '*=', '%=', '/=', '**='), self::TRANSLATE, self::CASE_SENSITIVE)
             ->back('result');
        $this->prepareQuery();

        // class a implements b { function ($a = int) {} }
        $this->checkOverwrittenArgumentType($intFnp);

        // function ($a = int)
        $this->checkArgumentDefaultValues($intAtoms);

        // function ($a) { bar($a);} function bar(array $b) {}
        $this->checkRelayedArgument(array('Scalartypehint'), $intFnp);

        // function ($a) { pow($a, 2);}
        $this->checkRelayedArgumentToPHP('int');

        // argument validation
        $this->checkArgumentValidation(array('\\is_int'), $intAtoms);

        // (int) or intval
        $this->checkCastArgument('T_INT_CAST', array('\\intval'));

        // foo(1); function foo($a) {}
        $this->checkCallingArgumentType(array('Integer'));

        // argument because used in a specific operation
        // $arg && ''
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Addition', 'Power', 'Multiplication'))
             ->back('result');
        $this->prepareQuery();

        // May also cover if( $arg).,
        // May also cover coalesce, ternary.
        // short assignations
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;


class CouldBeBoolean extends CouldBeType {
    public function analyze(): void {
        $booleanAtoms = array('Comparison', 'Logical', 'Boolean', 'Not');
        $boolFnp = array('\\bool');

        // property : based on default value (created or not)
        $this->checkPropertyDefault($booleanAtoms, array('T_SPACESHIP'));

        $this->checkPropertyRelayedDefault($booleanAtoms);

        // property relayed typehint
        $this->checkPropertyRelayedTypehint(array('Scalartypehint'), $boolFnp);

        // property relayed typehint
        $this->checkPropertyWithCalls(array('Scalartypehint'), $boolFnp);
        $this->checkPropertyWithPHPCalls('bool');

        // return type
        $this->checkReturnedAtoms($booleanAtoms, array('T_SPACESHIP'));

        $this->checkReturnedCalls(array('Scalartypehint'), $boolFnp);

        $this->checkReturnedPHPTypes('bool');

        $this->checkReturnedDefault($booleanAtoms);

        $this->checkReturnedTypehint(array('Scalartypehint'), $boolFnp);

        // argument type
        $this->checkArgumentUsage(array('Variablearray'));

        // class a implements b { function () : bool {} }
        $this->checkOverwrittenReturnType($boolFnp);

        // argument type
        // class a implements b { function ($a = bool) {} }
        $this->checkOverwrittenArgumentType($boolFnp);

        // function ($a = array())
        $this->checkArgumentDefaultValues($booleanAtoms);

        // function ($a) { bar($a);} function bar(array $b) {}
        $this->checkRelayedArgument(array('Scalartypehint'), $boolFnp);

        // function ($a) { array_diff($a);}
        $this->checkRelayedArgumentToPHP('bool');

        // argument validation
        $this->checkArgumentValidation(array('\\is_bool'), $booleanAtoms);

        // argument because used in a specific operation
        // $arg && ''
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->analyzerIsNot('self')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('LEFT', 'RIGHT'))
             ->atomIs(array('Logical', 'Not'))
             ->back('result');
        $this->prepareQuery();

        // May also cover if( $arg).,
        // May also cover coalesce, ternary.
        // short assignations
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;

class CouldBeArray extends CouldBeType {
    // dependsOn is in CouldBeType class

    public function analyze(): void {
        $arrayFnp = array('\\array');

        // property with default
        $this->checkPropertyDefault(array('Arrayliteral'));

        // property relayed default
        $this->checkPropertyRelayedDefault(array('Arrayliteral'));

        // property relayed typehint
        $this->checkPropertyRelayedTypehint(array('Scalartypehint'), $arrayFnp);

        // property relayed typehint
        $this->checkPropertyWithCalls(array('Scalartypehint'), $arrayFnp);
        $this->checkPropertyWithPHPCalls('array');

        // return type
        $this->checkReturnedAtoms(array('Arrayliteral'));

        $this->checkReturnedCalls(array('Scalartypehint'), $arrayFnp);

        $this->checkReturnedPHPTypes('array');

        $this->checkReturnedDefault(array('Arrayliteral'));

        $this->checkReturnedTypehint(array('Scalartypehint'), $arrayFnp);

        // class a implements b { function () : array {} }
        $this->checkOverwrittenReturnType($arrayFnp);

        // argument type
        // class a implements b { function ($a = array) {} }
        $this->checkOverwrittenArgumentType($arrayFnp);

        // $arg[]
        $this->checkArgumentUsage(array('Variablearray'));

        // function ($a = array())
        $this->checkArgumentDefaultValues(array('Arrayliteral'));

        // function ($a) { bar($a);} function bar(array $b) {}
        $this->checkRelayedArgument(array('Scalartypehint'), $arrayFnp);

        // function ($a) { array_diff($a);}
        $this->checkRelayedArgumentToPHP('array');

        // is_string
        $this->checkArgumentValidation(array('\\is_array'), array('Arrayliteral'));

        // argument because used in a specific operation with ...
        $this->atomIs(array('Parameter', 'Ppp'))
             ->not(
                $this->side()
                     ->outIs('TYPEHINT')
                     ->atomIs('Scalartypehint')
                     ->fullnspathIs('\\array')
             )
             ->outIs(array('PPP', 'NAME'))
             ->as('results')
             ->outIs('DEFINITION')
             ->hasIn('ARGUMENT') // functioncall or array
             ->is('variadic', true)
             ->back('results');
        $this->prepareQuery();

        $this->atomIs(self::FUNCTIONS_ALL)
             ->not(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->atomIs('Scalartypehint')
                     ->fullnspathIs('\\array')
             )
             ->outIs('DEFINITION')
             ->hasIn('ARGUMENT') // functioncall or array
             ->is('variadic', true)
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;


class CouldBeCIT extends CouldBeType {
    public function analyze(): void {
        $citOperations = array('New', 'Clone');
        $citAtoms      = array('Identifier', 'Nsname', 'Static', 'Self', 'Parent');

        // property : based on default value (created or not)
        $this->checkPropertyDefault($citOperations);

        $this->checkPropertyRelayedDefault($citOperations);

        // property relayed typehint
        $this->checkPropertyRelayedTypehint($citAtoms, array());

        // property relayed typehint
        $this->checkPropertyWithCalls($citAtoms, array());

        //Not possible ATM
//        $this->checkPropertyWithPHPCalls('bool');

//Cas de syntaxe : utilisé dans des syntaxes objets


        // return type
        $this->checkReturnedAtoms($citOperations);

        $this->checkReturnedCalls($citAtoms, array());

//        $this->checkReturnedPHPTypes('bool');

//        $this->checkReturnedDefault($booleanAtoms);

        $this->checkReturnedTypehint($citAtoms, array());

        // argument type
        // $arg . ''
        $this->checkArgumentUsage(array('Variablearray'));

        // function ($a = array())
        $this->checkArgumentDefaultValues($citOperations);

        // function ($a) { bar($a);} function bar(array $b) {}
        $this->checkRelayedArgument($citAtoms, array());

        // function ($a) { array_diff($a);}
//        $this->checkRelayedArgumentToPHP('Nsname');
//        $this->checkRelayedArgumentToPHP('Identifier');

        // argument validation
        $this->checkArgumentValidation(array('\\is_object'), array());

        // foo(new A); function foo($a) {}
        $this->checkCallingArgumentType(array('New', 'Nsname', 'Identifier', 'Clone'));

        // $arg instanceof B
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs('VARIABLE')
             ->atomIs('Instanceof')
             ->back('result');
        $this->prepareQuery();

        // argument because used in an object or static syntax
        // foo($arg) { $arg->o; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs(array('OBJECT', 'CLASS'))
             ->atomIs(array('Member', 'Methodcall', 'Staticmethodcall', 'Staticclass', 'Staticproperty', 'Staticconstant'))
             ->back('result');
        $this->prepareQuery();

        // throw $arg  (An exception must be an object)
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->as('result')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->inIs('THROW')
             ->atomIs('Throw')
             ->back('result');
        $this->prepareQuery();

        // May also cover if( $arg).,
        // May also cover coalesce, ternary.
        // short assignations
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ListOmissions extends Analyzer {
    public function dependsOn(): array {
        return array('Variables/VariableUsedOnceByContext',
                    );
    }

    public function analyze(): void {
        // list($a, $b, $c) = array(1,2,3);
        $this->atomIs('List')
             ->outIs('ARGUMENT')
             ->analyzerIs('Variables/VariableUsedOnceByContext');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class NoMagicWithArray extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/CreateMagicProperty',
                    );
    }

    public function analyze(): void {
        // class x { function __set() {} }
        // (new x)->a[] = 1;
        $this->atomIs(array('Array', 'Arrayappend'))
             ->outIs(array('VARIABLE', 'APPEND'))
             ->atomIs('Member')
             ->filter(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Magicmethod')
                     ->outIs('NAME')
                     ->codeIs('__set', self::TRANSLATE, self::CASE_INSENSITIVE)
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Traits;

use Exakat\Analyzer\Analyzer;

class UndefinedInsteadof extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/SetClassMethodRemoteDefinition',
                    );
    }

    public function analyze(): void {
        // class x { use t { t::undefined insteadof A; }}
        $this->atomIs('Staticmethod')
             ->hasNoIn('DEFINITION');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetArrayClassDefinition extends Complete {
    public function dependsOn(): array {
        return array('Complete/PropagateCalls',
                    );
    }

    public function analyze(): void {
        // array(\x, foo)
        $this->atomIs('Arrayliteral', self::WITHOUT_CONSTANTS)
              ->is('count', 2)
              ->outWithRank('ARGUMENT', 1)
              ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
              ->has('noDelimiter')
              ->savePropertyAs('noDelimiter', 'method')
              ->back('first')
              ->outWithRank('ARGUMENT', 0)
              ->atomIs(array('String', 'Heredoc', 'Concatenation', 'Staticclass'), self::WITH_CONSTANTS)
              ->outIsIE('CLASS') // For Staticclass only
              ->inIs('DEFINITION')
              ->atomIs('Class')
              ->outIs(array('MAGICMETHOD', 'METHOD'))
              ->atomIs(array('Method', 'Magicmethod'))
              ->outIs('NAME')
              ->samePropertyAs('fullcode', 'method', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // array(\x, foo)
        $this->atomIs('Arrayliteral', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->is('count', 2)
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->has('noDelimiter')
             ->savePropertyAs('noDelimiter', 'method')
             ->back('first')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->outIs('DEFAULT')
             ->atomIs('New')
             ->outIs('NEW')
             ->inIs('DEFINITION')
             ->atomIs('Class')
             ->outIs(array('MAGICMETHOD', 'METHOD'))
             ->atomIs(array('Method', 'Magicmethod'))
             ->outIs('NAME')
             ->samePropertyAs('fullcode', 'method', self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // array($this, foo)
        $this->atomIs('Arrayliteral', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->is('count', 2)
             ->outWithRank('ARGUMENT', 1)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             ->has('noDelimiter')
             ->savePropertyAs('noDelimiter', 'method')
             ->back('first')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('This')
             ->goToClass()
             ->atomIs('Class')
             ->outIs(array('MAGICMETHOD', 'METHOD'))
             ->atomIs(array('Method', 'Magicmethod'))
             ->outIs('NAME')
             ->samePropertyAs('fullcode', 'method', self::CASE_INSENSITIVE)
             ->inIs('NAME')
             ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // Link to the actual method
        $this->atomIs('Arrayliteral', self::WITHOUT_CONSTANTS)
             ->is('count', 2)
             ->inIs('DEFINITION')
             ->atomIs(array('Method', 'Magicmethod'))
             ->as('method')
             ->back('first')

             ->inIs('NAME')
             ->atomIs('Functioncall')
             ->addEFrom('DEFINITION', 'method');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class UselessInstruction extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/SetClassMethodRemoteDefinition',
                     'Classes/IsaMagicProperty',
                    );
    }

    public function analyze(): void {
        // Structures that should be put somewhere, and never left alone
        $this->atomIs('Sequence')
             ->hasNoIn('FINAL')
             ->hasNoParent('Match', array('CODE', 'EXPRESSION', 'CASES'))
             ->outIs('EXPRESSION')
             ->analyzerIsNot('Classes/IsaMagicProperty')
             ->atomIs(array('Array', 'Addition', 'Multiplication', 'Member', 'Staticproperty', 'Boolean',
                            'Magicconstant', 'Staticconstant', 'Integer', 'Float', 'Sign', 'Nsname',
                            'Identifier', 'String', 'Instanceof', 'Bitshift', 'Comparison', 'Null', 'Logical', 'Bitoperation',
                            'Heredoc', 'Power', 'Coalesce', 'Ternary', 'Variable', 'Arrayliteral', 'New', 'Match', 'Spaceship'))
             ->noAtomInside(array('Functioncall', 'Staticmethodcall', 'Methodcall', 'Assignation', 'Defineconstant'));
        $this->prepareQuery();

        // foreach($i = 0; $i < 10, $j < 20; $i++)
        $this->atomIs('For')
             ->outIs('FINAL')
             ->outWithoutLastRank()
             ->atomIs(array('Array', 'Addition', 'Multiplication', 'Member', 'Staticproperty', 'Boolean',
                            'Magicconstant', 'Staticconstant', 'Integer', 'Float', 'Sign', 'Nsname',
                            'Identifier', 'String', 'Instanceof', 'Bitshift', 'Comparison', 'Null', 'Logical',
                            'Heredoc', 'Power', 'Coalesce', 'New'))
             ->noAtomInside(array('Functioncall', 'Staticmethodcall', 'Methodcall', 'Assignation', ));
        $this->prepareQuery();

        $methods = $this->methods->getFunctionsReferenceArgs();
        $functions = array();
        foreach($methods as $method) {
            $functions[$method['function']] = 1;
        }

        /*
        // foo(1) // except for functions with references
        // Too soon : this must skip functions with side effects : ini_set, echo, rmdir, unlink, etc.
        $this->atomIs('Sequence')
             ->hasNoIn('FINAL')
             ->outIs('EXPRESSION')
             ->atomIs('Functioncall')
             ->fullnspathIsNot(makeFullnspath(array_keys($functions)))
             ->hasIn('DEFINITION')
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('ARGUMENT')
                     ->is('reference', true)
             )
             ->noAtomInside(array('Functioncall', 'Staticmethodcall', 'Methodcall', 'Assignation', 'New', ));
        $this->prepareQuery();
        */

/*
        // too soon
        // s::foo(1)
        $this->atomIs('Sequence')
             ->hasNoIn('FINAL')
             ->outIs('EXPRESSION')
             ->atomIs(array('Methodcall', 'Staticmethodcall'))
             ->hasIn('DEFINITION')
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('ARGUMENT')
                     ->is('reference', true)
             )
             ->noAtomInside(array('Functioncall', 'Staticmethodcall', 'Methodcall', 'Assignation', 'New', ));
        $this->prepareQuery();
*/

        // -$x = 3
        $this->atomIs('Assignation')
             ->outIs('LEFT')
             ->atomIs('Sign');
        $this->prepareQuery();

        // closures that are not assigned to something (argument or variable)
        $this->atomIs('Sequence')
             ->outIs('EXPRESSION')
             ->atomIs(array('Closure', 'Arrowfunction'));
        $this->prepareQuery();

        // return $a++; (unless it is an argument/use by reference)
        // May also check if it is static or global (those stays).
        $this->atomIs('Return')
             ->outIs('RETURN')
             ->atomIs('Postplusplus')
             ->outIs('POSTPLUSPLUS')
             ->atomIsNot(array('Member', 'Staticproperty'))
             ->not(
                $this->side()
                     ->atomIs('Variable')
                     ->inIs('DEFINITION')
                     ->outIs('DEFINITION')
                     ->atomIs(array('Staticdefinition', 'Globaldefinition'))
              )
             ->not(
                $this->side()
                     ->atomIs('Variable')
                     ->inIs('DEFINITION')
                     ->inIsIE('NAME')
                     ->is('reference', true)
              )
             ->back('first');
        $this->prepareQuery();

        // return an argument that is also a reference
        $this->atomIs('Return')
             ->analyzerIsNot('self')
             ->outIs('RETURN')
             ->atomIs('Postplusplus')
             ->outIs('POSTPLUSPLUS')
             ->atomIs('Variable')
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('DEFINITION')
                     ->atomIs(array('Staticdefinition', 'Globaldefinition'))
              )
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->inIsIE('NAME')
                     ->is('reference', true)
             )
             ->back('first');
        $this->prepareQuery();

        // return an assigned variable
        // todo : add support for static, referenc argument, global
        $this->atomIs('Return')
             ->analyzerIsNot('self')
             ->atomInsideNoDefinition('Assignation')
             ->outIs('LEFT')
             ->atomIsNot(array('Member', 'Staticproperty', 'Phpvariable'))
             ->hasNoChildren(array('Member', 'Staticproperty', 'Phpvariable'), array('VARIABLE'))
             ->hasNoChildren(array('Member', 'Staticproperty', 'Phpvariable'), array('VARIABLE', 'VARIABLE'))
             ->savePropertyAs('code', 'variable')
            // It is not an argument with reference
             ->isReferencedArgument('variable')
            // it is not a global nor a static
             ->not(
                $this->side()
                     ->atomIs('Variable')
                     ->inIs('DEFINITION')
                     ->outIs('DEFINITION')
                     ->atomIs(array('Staticdefinition', 'Globaldefinition'))
              )
             ->back('first');
        $this->prepareQuery();

        // array_merge($a); one argument is useless.
        $this->atomFunctionIs('\\array_replace')
             ->isLess('count', 2)
             ->outWithRank('ARGUMENT',0)
             ->isNot('variadic', true)
             ->back('first');
        $this->prepareQuery();

        // foreach(@$a as $b);
        $this->atomIs('Foreach')
             ->outIs('SOURCE')
             ->is('noscream', true);
        $this->prepareQuery();

        // @$x = 3;
        $this->atomIs('Assignation')
             ->outIs('LEFT')
             ->is('noscream', true);
        $this->prepareQuery();

        // Closure with some operations
        $this->atomIs('Function')
             ->inIs('LEFT')
             ->atomIs(array('Addition', 'Multiplication', 'Power'))
             ->back('first');
        $this->prepareQuery();

        // $x = 'a' . function ($a) {} (Concatenating a closure...)
        $this->atomIs('Function')
             ->inIs('CONCAT')
             ->atomIs('Concatenation')
             ->back('first');
        $this->prepareQuery();

        // New in a instanceof (with/without parenthesis)
        $this->atomIs('New')
             ->inIsIE(array('CODE', 'RIGHT'))
             ->inIs('VARIABLE')
             ->atomIs('Instanceof')
             ->back('first');
        $this->prepareQuery();

        // New in a clone
        $this->atomIs('New')
             ->inIsIE(array('CODE', 'CLONE'))
             ->atomIs('Clone');
        $this->prepareQuery();

        // Empty string in a concatenation
        $this->atomIs('Concatenation')
             ->outIs('CONCAT')
             ->outIsIE('CODE') // skip parenthesis
             ->codeIs(array("''", '""'))
             ->back('first');
        $this->prepareQuery();

        // array_unique(array_keys())
        $this->atomFunctionIs('\\array_unique')
             ->outWithRank('ARGUMENT', 0)
             ->functioncallIs('\\array_keys')
             ->back('first');
        $this->prepareQuery();

        $this->atomFunctionIs('\\count')
             ->outWithRank('ARGUMENT', 0)
             ->functioncallIs(array('\\array_keys',
                                    '\\array_values',
                                    '\\array_flip',
                                    '\\array_fill',
                                    '\\array_walk',
                                    '\\array_map',
                                    '\\array_change_key_case',
                                    '\\array_combine',
                                    '\\array_multisort',
                                    '\\array_replace',
                                    '\\array_reverse',
                                    ))
             ->back('first');
        $this->prepareQuery();

        // $a = $b ? 'c' : $a = 3;
        $this->atomIs('Assignation')
             ->outIs('LEFT')
             ->savePropertyAs('fullcode', 'var')
             ->back('first')

             ->outIs('RIGHT')
             ->atomIs(array('Ternary', 'Coalesce'))
             ->outIs(array('THEN', 'ELSE', 'RIGHT'))
             ->atomIs('Assignation')
             ->outIs('LEFT')
             ->samePropertyAs('fullcode', 'var')
             ->back('first');
        $this->prepareQuery();

        // $a ?? null
        $this->atomIs('Coalesce')
             ->analyzerIsNot('self')
             ->outIs('RIGHT')
             ->outIsIE('CODE')
             ->atomIsNot(array('Coalesce', 'Ternary')) // ternary should be check on both sides of the branch
             ->atomIs('Null', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Constants;

use Exakat\Analyzer\Analyzer;

class UndefinedConstants extends Analyzer {
    public function dependsOn(): array {
        return array('Constants/ConstantUsage',
                     'Constants/IsExtConstant',
                     'Constants/CustomConstantUsage',
                     'Complete/SetStringMethodDefinition'
                    );
    }

    public function analyze(): void {
        // echo UNDEFINED_CONSTANT
        $this->analyzerIs('Constants/ConstantUsage')
             ->atomIsNot(array('Boolean', 'Null'))
             ->hasNoIn(array('AS', 'TYPEHINT', 'RETURNTYPE', 'GOTOLABEL', 'GOTO'))
             ->analyzerIsNot(array('Constants/CustomConstantUsage',
                                   'Constants/IsExtConstant',
                                   ))
             // skipping "$a::$b"
             ->not(
                $this->side()
                     ->outIs('CONCAT')
                     ->atomIs(array('Variable', 'Member'))
             )

             ->not(
                $this->side()
                     ->atomIs('String')
                     ->hasIn('ARGUMENT')
                     ->is('rank', 0)
                     ->inIs('ARGUMENT')
                     ->atomIs('Functioncall')
                     ->fullnspathIs('\\defined')
             )

             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true)
             ->isNotIgnored()
             ->hasNoIn('DEFINITION');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class UndefinedConstants extends Analyzer {
    public function dependsOn(): array {
        return array('Classes/DefinedConstants',
                    );
    }

    public function analyze(): void {
        // A::Undefined
        $this->atomIs('Staticconstant')
             ->analyzerIsNot('Classes/DefinedConstants')
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->isNot('isStub', true)

             // cases for array $a[1]
             ->outIs('CLASS')
             ->atomIsNot('Array')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class Deprecated extends Analyzer {

    public function analyze(): void {
        $ini = (object) $this->loadIni('deprecated.ini');

        $ini->functions = array_filter($ini->functions);
        $ini->functions = array_unique($ini->functions);
        $ini->functions = array_values($ini->functions);
        $functions = makeFullNsPath($ini->functions);

        // direct call to an old global function
        $this->atomFunctionIs($functions);
        $this->prepareQuery();

        // fallback call to an old global function
        $this->atomIs('Functioncall')
             ->hasNoIn('DEFINITION')
             ->has('fullnspath')
             ->raw(<<<'GREMLIN'
filter{
    it.get().value('fullnspath').tokenize('\\').size() > 1;
}.filter{
    name = '\\' + it.get().value('fullnspath').tokenize('\\').last();
    name in ***;
}
GREMLIN
, $functions);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class WrongNumberOfArguments extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateCalls',
                     'Complete/MakeClassMethodDefinition',
                     'Complete/CreateMagicProperty',
                     'Complete/SetStringMethodDefinition',
                     'Complete/SetArrayClassDefinition',
                     'Complete/FollowClosureDefinition',
                     'Complete/SetClassMethodRemoteDefinition',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                     'Complete/SetClassRemoteDefinitionWithGlobal',
                     'Complete/SetClassRemoteDefinitionWithInjection',
                     'Complete/SetClassRemoteDefinitionWithLocalNew',
                     'Complete/SetClassRemoteDefinitionWithParenthesis',
                     'Complete/SetClassRemoteDefinitionWithReturnTypehint',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                     'Functions/VariableArguments',
                     'Complete/VariableTypehint',
                     'Complete/IsStubStructure',
                     'Complete/SolveTraitMethods',
                    );
    }

    public function analyze(): void {
        // this is for functions defined within PHP
        $functions = $this->methods->getFunctionsArgsInterval();
        $argsMins = array();
        $argsMaxs = array();

        foreach($functions as $function) {
            $argsMins[makeFullNsPath($function['name'])] = $function['args_min'];

            if ($function['args_max'] < \MAX_ARGS) {
                $argsMaxs[makeFullNsPath($function['name'])] = $function['args_max'];
            }
        }

        $stubs = exakat('stubs')->getFunctionsArgsInterval();
        foreach($stubs as $function) {
            $argsMins[makeFullNsPath($function['name'])] = $function['args_min'];

            if ($function['args_max'] < \MAX_ARGS) {
                $argsMaxs[makeFullNsPath($function['name'])] = $function['args_max'];
            }
        }

       $this->atomFunctionIs(array_keys($argsMins))
            ->savePropertyAs('fullnspath', 'fnp')
            ->hasNoVariadicArgument()
            ->isLessHash('count', $argsMins, 'fnp')
            ->back('first');
       $this->prepareQuery();

       $this->atomFunctionIs(array_keys($argsMaxs))
            ->savePropertyAs('fullnspath', 'fnp')
            ->hasNoVariadicArgument()
            ->isMoreHash('count', $argsMaxs, 'fnp')
            ->back('first');
       $this->prepareQuery();

        // this is for custom functions
        // function foo(1, 2)
        $this->atomIs(self::FUNCTIONS_CALLS)
             ->outIsIE('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->inIsIE('METHOD') // for methods calls, static or not.
             ->inIsIE('NEW')
             ->inIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->isMore('args_min', 'args_count')
             ->back('first');
        $this->prepareQuery();

        // this is for custom functions
        // foo(...[1,2,])
        $this->atomIs(self::FUNCTIONS_CALLS)
             ->outIsIE('METHOD') // for methods calls, static or not.
             ->outWithRank('ARGUMENT', 0)
             ->is('variadic', true)
             ->savePropertyAs('count', 'args_count')
             ->back('first')
             ->inIsIE('NEW')
             ->inIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->isLess('args_max', 'args_count')
             ->back('first');
        $this->prepareQuery();

        // this is for custom functions
        // foo(...[1,2,])
        $this->atomIs(self::FUNCTIONS_CALLS)
             ->outIsIE('METHOD') // for methods calls, static or not.
             ->outWithRank('ARGUMENT', 0)
             ->is('variadic', true)
             ->savePropertyAs('count', 'args_count')
             ->back('first')
             ->inIsIE('NEW')
             ->inIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->isMore('args_max', 'args_count')
             ->back('first');
        $this->prepareQuery();

        // new A
        // new A()
        // new class() { function __construct($a) {}}
/*        $this->atomIs('New')
             ->outIs('NEW')
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->inIs('DEFINITION')
             ->atomIs('Magicmethod')
             ->analyzerIsNot('Functions/VariableArguments')
             ->isLess('args_min', 'args_count')
             ->back('first');
        $this->prepareQuery();
*/
        // this is for custom functions
        // new class() { function __construct($a) {}}
        $this->atomIs('New')
             ->outIs('NEW')
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->outIs('DEFINITION')
             ->atomIs('Magicmethod')
             ->analyzerIsNot('Functions/VariableArguments')
             ->isLess('args_count', 'args_min')
             ->back('first');
        $this->prepareQuery();

        // new A($a)
        // new class($a) { function __construct() {}}
        $this->atomIs('New')
             ->outIs('NEW')
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->outIs('DEFINITION')
             ->atomIs('Magicmethod')
             ->analyzerIsNot('Functions/VariableArguments')
             ->isMore('args_count', 'args_max')
             ->back('first');
        $this->prepareQuery();

        // new classe(...[1,2,])
        $this->atomIs('New')
             ->outIs('NEW')
             ->outWithRank('ARGUMENT', 0)
             ->is('variadic', true)
             ->savePropertyAs('count', 'args_count')
             ->inIs('ARGUMENT')
             ->outIs('DEFINITION')
             ->atomIs('Magicmethod')
             ->analyzerIsNot('Functions/VariableArguments')
             ->isMore('args_max', 'args_count')
             ->back('first');
        $this->prepareQuery();

        // new self($args)
        $this->atomIs(array('Self', 'Parent'))
             ->hasIn('NEW')
             ->outIsIE('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->inIsIE('METHOD') // for methods calls, static or not.
             ->inIsIE('NEW')
             ->inIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->isMore('args_min', 'args_count')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(self::FUNCTIONS_CALLS)
             ->outIsIE('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->inIsIE('METHOD') // for methods calls, static or not.
             ->inIsIE('NEW')
             ->inIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/VariableArguments')
             ->isLess('args_max', 'args_count')
             ->back('first');
        $this->prepareQuery();

        // new self($args)
        $this->atomIs(array('Self', 'Parent'))
             ->hasIn('NEW')
             ->outIsIE('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()
             ->savePropertyAs('count', 'args_count')
             ->inIsIE('METHOD') // for methods calls, static or not.
             ->inIsIE('NEW')
             ->inIs('DEFINITION')
             ->analyzerIsNot('Functions/VariableArguments')
             ->isLess('args_max', 'args_count')
             ->back('first');
        $this->prepareQuery();

        // new sqlite3()
        $news = $this->methods->getNewArgsInterval();
        $argsMins = array();
        $argsMaxs = array();
        foreach($news as $new) {
            $argsMins[makeFullNsPath($new['class'])] = $new['args_min'];

            if ($function['args_max'] < \MAX_ARGS) {
                $argsMaxs[makeFullNsPath($new['class'])] = $new['args_max'];
            }
        }

        $stubs = exakat('stubs')->getConstructorsArgsInterval();
        foreach($stubs as $function) {
            list($classe, ) = explode('::', $function['name'], 2);
            $classe = makeFullNsPath($classe);
            $argsMins[$classe] = $function['args_min'];

            if ($function['args_max'] < \MAX_ARGS) {
                $argsMaxs[$classe] = $function['args_max'];
            }
        }

        $this->atomIs('New')
             ->outIs('NEW')
             ->savePropertyAs('fullnspath', 'fnp')
             ->hasNoVariadicArgument()
             ->isLessHash('count', $argsMins, 'fnp')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('New')
             ->outIs('NEW')
             ->savePropertyAs('fullnspath', 'fnp')
             ->hasNoVariadicArgument()
             ->isMoreHash('count', $argsMaxs, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // (new sqlite3())->fetchArray(1,2,3)
        $methods = $this->methods->getMethodsArgsInterval();
        $argsMins = array();
        $argsMaxs = array();
        foreach($methods as $method) {
            $argsMins[makeFullNsPath($method['class']) . '::' . mb_strtolower($method['name'])] = $method['args_min'];

            if ($function['args_max'] < \MAX_ARGS) {
                $argsMaxs[makeFullNsPath($method['class']) . '::' . mb_strtolower($method['name'])] = $method['args_max'];
            }
        }
        $stubs = exakat('stubs')->getMethodsArgsInterval();
        foreach($stubs as $methods) {
            $argsMins[makeFullNsPath($methods['name'])] = $methods['args_min'];

            if ($function['args_max'] < \MAX_ARGS) {
                $argsMaxs[makeFullNsPath($methods['name'])] = $methods['args_max'];
            }
        }
        // tester avec classes, interfaces et traits
        // tester avec methods statiques

        $this->atomIs('Methodcall')
             ->outIs('OBJECT')
             ->goToTypehint()
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isLessHash('count', $argsMins, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // Method in a trait
        // For traits, one must go to the definition, and find the trait in the class.
        $this->atomIs('Methodcall')
             ->outIs('OBJECT')
             ->goToTypehint()
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('USE')
             ->outIs('USE')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isLessHash('count', $argsMins, 'fnp')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs(self::METHOD_CALLS)
             ->outIs('OBJECT')
             ->goToTypehint()
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isMoreHash('count', $argsMaxs, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // Method in a trait
        // For traits, one must go to the definition, and find the trait in the class.
        $this->atomIs('Methodcall')
             ->outIs('OBJECT')
             ->goToTypehint()
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('USE')
             ->outIs('USE')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isMoreHash('count', $argsMaxs, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // Statimc methods
        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isLessHash('count', $argsMins, 'fnp')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('USE')
             ->outIs('USE')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isLessHash('count', $argsMins, 'fnp')
             ->back('first');
        $this->prepareQuery();


        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isMoreHash('count', $argsMaxs, 'fnp')
             ->back('first');
        $this->prepareQuery();

        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->inIs('DEFINITION')
             ->goToAllParentsTraits(self::INCLUDE_SELF)
             ->outIs('USE')
             ->outIs('USE')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('METHOD') // for methods calls, static or not.
             ->hasNoVariadicArgument()

             ->outIs('NAME')
             ->savePropertyAs('fullcode', 'name')
             ->inIs('NAME')

             ->raw('sideEffect{ fnp = fnp + "::" + name.toLowerCase(); }')

             ->isMoreHash('count', $argsMaxs, 'fnp')
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class MakeFunctioncallWithReference extends Complete {
    public function dependsOn(): array {
        return array('Complete/SetClassMethodRemoteDefinition',
                     'Complete/SetClassRemoteDefinitionWithLocalNew',
                     'Complete/SetClassRemoteDefinitionWithGlobal',
                     'Complete/SetClassRemoteDefinitionWithInjection',
                     'Complete/SetClassRemoteDefinitionWithParenthesis',
                     'Complete/SetClassRemoteDefinitionWithReturnTypehint',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                     'Complete/PropagateCalls',
                    );
    }

    public function analyze(): void {
        // Case of PHP native functions
        $methods = $this->readStubs('getFunctionsReferenceArgs');
        $functions = array();
        foreach($methods as $method) {
            array_collect_by($functions, $method['position'], makeFullNsPath($method['function']));
        }

        foreach($functions as $position => $calls) {
            $this->atomFunctionIs($calls)
                 ->outWithRank('ARGUMENT', $position)
                 ->setProperty('isModified', true);
            $this->prepareQuery();
        }
        
        // @todo : extends to methods and static methods 

        // Case of Custom native functions
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->is('reference', true)
             ->goToParameterUsage()
             ->setProperty('isModified', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Data\Methods;

class UselessCasting extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateCalls',
                     'Complete/VariableTypehint',
                    );
    }

    public function analyze(): void {
        // Function returning a type, then casted to that type
        $casts = array('T_STRING_CAST'  => 'string',
                       'T_BOOL_CAST'    => 'bool',
                       'T_INT_CAST'     => 'int',
                       'T_ARRAY_CAST'   => 'array',
                       'T_DOUBLE_CAST'  => 'float'
                  );

        $returnTypes = $this->methods->getFunctionsByReturn(Methods::STRICT);

        foreach($casts as $token => $type) {
            if (is_array($type)) {
                $returned = array();
                foreach($type as $t) {
                    $returned[] = $returnTypes[$t];
                }
                $returned = array_merge(...$returned);
            } else {
                $returned = $returnTypes[$type];
            }

            // native PHP functions
            $this->atomIs('Cast')
                 ->analyzerIsNot('self')
                 ->tokenIs($token)
                 ->outIs('CAST')
                 ->outIsIE('CODE') // In case there are some parenthesis
                 ->atomIs('Functioncall')
                 ->fullnspathIs($returned)
                 ->back('first');
            $this->prepareQuery();

            // custom user methods
            $this->atomIs('Cast')
                 ->analyzerIsNot('self')
                 ->tokenIs($token)
                 ->outIs('CAST')
                 ->outIsIE('CODE') // In case there are some parenthesis
                 ->atomIs(self::CALLS)
                 ->inIs('DEFINITION')
                 ->not(
                    $this->side()
                         ->outIs('RETURNTYPE')
                         ->count()
                         ->isMore(1)
                 )
                 ->outIs('RETURNTYPE')
                 ->is('fullnspath', makeFullNsPath($type))
                 ->back('first');
            $this->prepareQuery();
        }

        // function foo(array $a) { (array) $a; }
        $this->atomIs('Cast')
             ->analyzerIsNot('self')
             ->savePropertyAs('token', 'cast')
             ->outIs('CAST')
             ->outIsIE('CODE') // In case there are some parenthesis
             ->goToTypehint()
             ->has('fullnspath')
             ->raw(<<<'GREMLIN'
filter{ 
    (cast == "T_ARRAY_CAST"             && it.get().value("fullnspath") == "\\array")  || 
    (cast == "T_INT_CAST"               && it.get().value("fullnspath") == "\\int")    || 
    (cast == "T_DOUBLE_CAST"            && it.get().value("fullnspath") == "\\float")  || 
    (cast == "T_STRING"                 && it.get().value("fullnspath") == "\\object") || 
    (cast == "T_NAME_FULLY_QUALIFIED"   && it.get().value("fullnspath") == "\\object") || 
    (cast == "T_NAME_RELATIVE"          && it.get().value("fullnspath") == "\\object") || 
    (cast == "T_NAME_QUALIFIED"         && it.get().value("fullnspath") == "\\object") || 
    (cast == "T_BOOL_CAST"              && it.get().value("fullnspath") == "\\bool")   || 
    (cast == "T_STRING_CAST"            && it.get().value("fullnspath") == "\\string")
    ; 
}

GREMLIN
)
             ->inIs('TYPEHINT')
             ->is('typehint', 'one')
             ->back('first');
        $this->prepareQuery();

        // (bool) ($a > 2)
        $this->atomIs('Cast')
             ->tokenIs('T_BOOL_CAST')
             ->followParAs('CAST')
             ->atomIs('Comparison')
             ->back('first');
        $this->prepareQuery();

        // (int) 100
        $this->atomIs('Cast')
             ->tokenIs('T_INT_CAST')
             ->outIsIE('CODE')
             ->outIs('CAST')
             ->atomIsNot(array('Coalesce', 'Ternary'))
             ->atomIs(array('Integer', 'Multiplication', 'Power', 'Addition'), self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class KillsApp extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/SetClassRemoteDefinitionWithTypehint',
                     'Complete/SetClassRemoteDefinitionWithGlobal',
                     'Complete/SetClassRemoteDefinitionWithInjection',
                     'Complete/SetClassRemoteDefinitionWithLocalNew',
                     'Complete/SetClassRemoteDefinitionWithParenthesis',
                     'Complete/SetClassRemoteDefinitionWithReturnTypehint',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                    );
    }

    public function analyze(): void {
        // first round : only die and exit
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             // We need this straight in the main sequence, not deep in a condition
             ->outIs('EXPRESSION')
             ->atomIs('Exit')
             ->back('first');
        $this->prepareQuery();

        // #[NoReturn] as attribute
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ATTRIBUTE')
             ->fullnspathIs('\\noreturn')
             ->back('first');
        $this->prepareQuery();

        // @NoReturn as phpdoc
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('PHPDOC')
             ->regexIs('fullcode', '(?i)@noreturn')
             ->back('first');
        $this->prepareQuery();

        // second round
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             // We need this straight in the main sequence, not deep in a condition
             ->outIs('EXPRESSION')
             ->atomIs('Functioncall')
             ->inIs('DEFINITION')
             ->analyzerIs('self')
             ->back('first');
        $this->prepareQuery();

        // third round (Identical to the previous)
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             // We need this straight in the main sequence, not deep in a condition
             ->outIs('EXPRESSION')
             ->atomIs('Functioncall')
             ->analyzerIs('self')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class InvalidPackFormat extends Analyzer {
    public function dependsOn(): array {
        return array('Type/Pack',
                    );
    }


    public function analyze(): void {
        // pack('nvcT', $s)
        $this->atomFunctionIs('\\unpack')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             // Those steps weed out some non-formats
             ->analyzerIs('Type/Pack')
             ->raw('has("noDelimiter", neq(""))')
             // This regex include names in the format string, for unpacking
             ->regexIsNot('noDelimiter', '^([@0-9aAhHcCsSnviIlLNVqQJPfgGdeExXZ](\\\\*|\\\\d+)?(\\\\w+\\\\/?)?)+\$')
             ->back('first');
        $this->prepareQuery();

        $this->atomFunctionIs('\\pack')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(self::STRINGS_LITERALS, self::WITH_CONSTANTS)
             // Those steps weed out some non-formats
             ->analyzerIs('Type/Pack')
             ->raw('has("noDelimiter", neq(""))')
             // This regex include names in the format string, for packing
             ->regexIsNot('noDelimiter', '^([@0-9aAhHcCsSnviIlLNVqQJPfgGdeExXZ](\\\\*|\\\\d+)?)+\$')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class WrongReturnedType extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                     'Functions/IsGenerator',
                     'Complete/SetClassRemoteDefinitionWithGlobal',
                     'Complete/SetClassRemoteDefinitionWithInjection',
                     'Complete/SetClassRemoteDefinitionWithLocalNew',
                     'Complete/SetClassRemoteDefinitionWithParenthesis',
                     'Complete/SetClassRemoteDefinitionWithReturnTypehint',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                    );
    }

    public function analyze(): void {
    // @todo Generator, Iterator, Traversable, or iterable
    // @todo : missing support for return typehint from functions (custom and natives)
    // @todo : support union and intersection types

        // function foo() : A { return new A;}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')
             ->outIs('RETURNED')
             ->as('results')

             ->outIs('NEW')
             ->notSamePropertyAs('fullnspath', 'fqn')
             /*
             ->not(
                 $this->side()
                      ->inIs('DEFINITION')
                      ->goToAllImplements(self::INCLUDE_SELF)
                      ->samePropertyAs('fullnspath', 'fqn')
             )*/
             ->back('results')
             ->inIs('RETURN');
            $this->prepareQuery();

        // function foo() : A { return new A;}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIsNot(array('Void', 'Scalartypehint'))
             ->back('first')
             ->outIs('RETURNED')
             ->atomIs(array('Integer', 'String', 'Heredoc', 'Float', 'Null', 'Boolean', 'Arrayliteral'))
             ->inIs('RETURN');
        $this->prepareQuery();

        // function foo() : A { $a = 1; return $a;}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIsNot(array('Void', 'Scalartypehint'))
             ->back('first')
             ->outIs('RETURNED')
             ->as('results')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->inIsIE('NAME')
             ->outIs('TYPEHINT')
             ->atomIs('Void')
             ->inIs('TYPEHINT')
             ->outIs('DEFAULT')
             ->atomIs(array('Integer', 'String', 'Heredoc', 'Float', 'Null', 'Boolean', 'Arrayliteral', 'Void'))
             ->back('results')
             ->inIs('RETURN');
        $this->prepareQuery();

        // function foo() : A { $a = new B; return $a;}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIsNot(array('Void', 'Scalartypehint'))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')
             ->outIs('RETURNED')
             ->as('results')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->outIs('DEFAULT')
             ->atomIs('New')
             ->outIs('NEW')

              ->notSamePropertyAs('fullnspath', 'fqn')
              /*
            ->not(
                 $this->side()
                      ->inIs('DEFINITION')
                      ->goToAllImplements(self::INCLUDE_SELF)
                      ->samePropertyAs('fullnspath', 'fqn')
             )
             */
             ->back('results')
             ->inIs('RETURN')
             ->analyzerIsNot('self');
        $this->prepareQuery();

        // function foo(B $b) : A { return $b;}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIsNot(array('Void', 'Scalartypehint'))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')
             ->outIs('RETURNED')
             ->as('results')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->outIs('TYPEHINT')
             ->notSamePropertyAs('fullnspath', 'fqn')
             ->back('results')
             ->inIs('RETURN')
             ->analyzerIsNot('self');
        $this->prepareQuery();

        // PHP scalar types
        // Don't process void : it is checked at lint time
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->not(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->atomIs(array('Identifier', 'Nsname', 'Void'))
             )
             ->collectTypehints('types')
             ->outIs('RETURNED')
             ->hasIn('RETURN')
             ->as('results')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->atomIsNot(array('Variable', 'Staticproperty', 'Member', 'Functioncall', 'Methodcall', 'Staticmethodcall'))
             ->optional(
                $this->side()
                     ->atomIs(array('Identifier', 'Nsname'), self::WITH_CONSTANTS)
             )
             ->checkTypeWithAtom('types')
             ->back('results')
             ->inIs('RETURN')
             ->analyzerIsNot('self');
        $this->prepareQuery();

        //Relayed return types
        // Don't process void : it is checked at lint time
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIs('Scalartypehint')
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')
             ->outIs('RETURNED')
             ->hasIn('RETURN')
             ->as('results')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->atomIs(array('Functioncall', 'Methodcall', 'Staticmethodcall'))
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')
             ->savePropertyAs('fullnspath', 'fqn2')
             ->raw('filter{ fqn != fqn2; }')
             ->back('results')
             ->inIs('RETURN');
        $this->prepareQuery();

        // Type is not the argument type
        $this->atomIs(self::FUNCTIONS_ALL)
             ->analyzerIsNot('Functions/IsGenerator')
             ->outIs('RETURNTYPE')
             ->atomIs('Scalartypehint')
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')
             ->outIs('RETURNED')
             ->hasIn('RETURN')
             ->as('results')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->outIs('TYPEHINT')
             ->notSamePropertyAs('fullnspath', 'fqn')
             ->back('results')
             ->inIs('RETURN')
             ->analyzerIsNot('self');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class ScalarAreNotArrays extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/MakeClassMethodDefinition',
                     'Complete/CreateDefaultValues',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                     'Complete/ReturnTypehint',
                    );
    }

    public function analyze(): void {
        // With typehint
        // function foo(int $x) { echo $x[2]; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->outIs('TYPEHINT')
             ->fullnspathIs(array('\\int', '\\bool', '\\float', '\\null'))
             ->inIs('TYPEHINT')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->atomIs('Variablearray')
             ->inIs('VARIABLE');
        $this->prepareQuery();

        // With typehint
        // function foo($x = 2) { echo $x[2]; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->analyzerIsNot('self')
             ->outIs('DEFAULT')
             ->atomIs(array('Boolean', 'Integer', 'Float', 'Null'))
             ->inIs('DEFAULT')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->atomIs('Variablearray')
             ->inIs('VARIABLE')
             ->analyzerIsNot('self');
        $this->prepareQuery();

        // With return typehint
        // $a = foo(); echo $a[2]; function foo() : int {}
        $this->atomIs('Variablearray')
             ->inIs('DEFINITION')
             ->outIs('DEFAULT')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->inIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->fullnspathIs(array('\\int', '\\bool', '\\float', '\\void', '\\callable', '\\null'))
             ->back('first')
             ->inIs('VARIABLE')
             ->analyzerIsNot('self');
        $this->prepareQuery();

        // With argument's default value
        // function foo(int $x) { echo $x[2]; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->outIs('DEFAULT')
             ->fullnspathIs(array('\\int', '\\bool', '\\float', '\\null'))
             ->inIs('DEFAULT')
             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->atomIs('Variablearray')
             ->inIs('VARIABLE')
             ->analyzerIsNot('self');
        $this->prepareQuery();

        // foo(1.2); function foo($a) { $a[3]; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('DEFINITION')
             ->outIs('ARGUMENT')
             ->atomIs(array('Boolean', 'Integer', 'Float', 'Null'))
             ->goToParameterDefinition()

             ->outIs('NAME')
             ->outIs('DEFINITION')
             ->atomIs('Variablearray')
             ->inIs('VARIABLE')
             ->analyzerIsNot('self');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class IsAWithString extends Analyzer {
    public function dependsOn(): array {
        return array('Variables/IsLocalConstant',
                     'Complete/PropagateCalls',
                    );
    }

    public function analyze(): void {
        // is_a('a', 'b');
        $this->atomFunctionIs(array('\\is_a',
                                    '\\is_subclass_of',
                                   ))
             ->noChildWithRank('ARGUMENT', 2)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();

        // is_a('a', 'b', false);
        $this->atomFunctionIs(array('\\is_a',
                                    '\\is_subclass_of',
                                   ))
             ->outWithRank('ARGUMENT', 2)
             ->is('boolean', false)
             ->back('first')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String', self::WITH_CONSTANTS)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class MbstringUnknownEncoding extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                     'Complete/PropagateCalls',
                    );
    }

    public function analyze(): void {
        $encodings = $this->loadIni('mbstring_encodings.ini', 'encodings');

        $positions = array('\\mb_preferred_mime_name'  => array(0),
                           '\\mb_regex_encoding'       => array(0),

                           '\\mb_check_encoding'       => array(1),
                           '\\mb_chr'                  => array(1),
                           '\\mb_ord'                  => array(1),
                           '\\mb_scrub'                => array(1),
                           '\\mb_strlen'               => array(1),

                           '\\mb_convert_case'         => array(2),
                           '\\mb_convert_encoding'     => array(2),
                           '\\mb_convert_kana'         => array(2),
                           '\\mb_decode_numericentity' => array(2),
                           '\\mb_encode_numericentity' => array(2),
                           '\\mb_internal_encoding'    => array(2),
                           '\\mb_strcut'               => array(2),
                           '\\mb_strtolower'           => array(2),
                           '\\mb_strtoupper'           => array(2),
                           '\\mb_strwidth'             => array(2),
                           '\\mb_substr_count'         => array(2),

                           '\\mb_stripos'              => array(3),
                           '\\mb_stristr'              => array(3),
                           '\\mb_strpos'               => array(3),
                           '\\mb_strripos'             => array(3),
                           '\\mb_strrpos'              => array(3),
                           '\\mb_strrchr'              => array(3),
                           '\\mb_strrichr'             => array(3),
                           '\\mb_strstr'               => array(3),
                           '\\mb_substr'               => array(3),

                           '\\mb_strimwidth'           => array(4),
                          );

        //mb_check_encoding($x, 'UTF9');
        $this->atomFunctionIs(array_keys($positions))
             ->savePropertyAs('fullnspath', 'fnp')
             ->outIs('ARGUMENT')
             ->isHash('rank', $positions, 'fnp')
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->atomIs(array('String', 'Concatenation', 'Integer', 'Boolean', 'Null'), self::WITH_CONSTANTS)
             ->noDelimiterIsNot($encodings, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\FollowParAs;

class MbstringThirdArg extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                     'Complete/PropagateCalls',
                    );
    }

    public function analyze(): void {
        $encodings = $this->loadIni('mbstring_encodings.ini', 'encodings');

        $functions = array('\\mb_strrichr',
                           '\\mb_stripos' ,
                           '\\mb_strrpos' ,
                           '\\mb_strstr'  ,
                           '\\mb_stristr' ,
                           '\\mb_strpos'  ,
                           '\\mb_strripos',
                           '\\mb_strrchr' ,
                           '\\mb_strrichr',
                           '\\mb_substr'  ,
        );

        $this->atomFunctionIs($functions)
             ->outWithRank('ARGUMENT', 2)
             ->followParAs(FollowParAs::FOLLOW_NONE)
             ->atomIs(array('String', 'Concatenation'), self::WITH_CONSTANTS)
             ->noDelimiterIs($encodings, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class WrongTypeWithCall extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/PropagateConstants',
                     'Complete/FollowClosureDefinition',
                     'Complete/SetClassMethodRemoteDefinition',
                     'Complete/SetClassRemoteDefinitionWithParenthesis',
                    );
    }

    public function analyze(): void {
        // foo(1); function foo(string $s) {}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->savePropertyAs('rank', 'ranked')
             ->isNot('variadic', true)
             ->outIs('TYPEHINT')
             ->savePropertyAs('fullnspath', 'fnp')
             ->atomIs('Scalartypehint')
             ->back('first')

             ->outIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->as('results')
             ->outIsIE('METHOD')
             ->outWithRank('ARGUMENT', 'ranked')
             ->atomIs(array('Integer',
                            'String',
                            'Arrayliteral',
                            'Concatenation',
                            'Addition',
                            'Power',
                            'Float',
                            ), self::WITH_CONSTANTS)
             ->checkTypeWithAtom('fnp')
             ->back('results');
        $this->prepareQuery();

        // foo(new d); function foo(string $s) {}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->savePropertyAs('rank', 'ranked')
             ->isNot('variadic', true)
             ->not(
                $this->side()
                     ->outIs('TYPEHINT')
                     ->atomIsNot(array('Scalartypehint', 'Null'))
             )
             ->back('first')

             ->outIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->as('results')
             ->outIsIE('METHOD')
             ->outWithRank('ARGUMENT', 'ranked')
             ->atomIs(array('New',
                            'Clone',
                            ), self::WITH_VARIABLES)
             ->back('results');
        $this->prepareQuery();

        // calling a class-typed argument with a wrong class : include single type, union and intersections
        // foo(new Y); function foo(x $s) {}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->savePropertyAs('rank', 'ranked')
             ->isNot('variadic', true)
             ->not(
                $this->side()
                     ->outIs('TYPEHINT')
                     ->atomIsNot(array('Identifier', 'Nsname', 'Null'))
             )
             ->collectTypehints('typehints' )
             ->savePropertyAs('typehint', 't')
             ->back('first')

             ->outIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->as('results')
             ->outIsIE('METHOD')
             ->outWithRank('ARGUMENT', 'ranked')
             ->atomIs('New', self::WITH_VARIABLES)
             ->outIs('NEW')
             ->inIs('DEFINITION')
             ->isNotClassCompatible('typehints', 't')
             ->back('results');
        $this->prepareQuery();

        // calling a class-typed argument with a scalar
        // foo(1); function foo(x $s) {}
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->savePropertyAs('rank', 'ranked')
             ->isNot('variadic', true)
             ->not(
                $this->side()
                     ->outIs('TYPEHINT')
                     ->atomIsNot(array('Identifier', 'Nsname', 'Null'))
             )
             ->back('first')

             ->outIs('DEFINITION')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->as('results')
             ->outIsIE('METHOD')
             ->outWithRank('ARGUMENT', 'ranked')
             ->atomIs(array('Integer',
                            'String',
                            'Arrayliteral',
                            'Concatenation',
                            'Addition',
                            'Power',
                            'Float',
                            ), self::WITH_CONSTANTS)
             ->back('results');
        $this->prepareQuery();

        // calling a class-typed argument with self/static/parent
        // foo(new Z); function foo(self $s) {}

        // @todo
        // calling a class-typed argument with typed element (with typed property)
        // foo(new Z); function foo(self $s) {}
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class WrongTypedPropertyInit extends Analyzer {
    protected $phpVersion = '7.4+';

    public function dependsOn(): array {
        return array('Complete/PropagateCalls',
                     'Complete/FollowClosureDefinition',
                     'Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        // class x { a $a; function foo() { $this->a = new b()}}
        $this->atomIs('Propertydefinition')
             ->inIs('PPP')
             ->is('typehint', 'one')
             ->outIs('TYPEHINT')
             ->atomIsNot('Void')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('DEFAULT')
             ->atomIs('New')
             ->hasIn('RIGHT')
             ->outIs('NEW')
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->inIs('DEFINITION')
                             ->goToAllImplements(self::INCLUDE_SELF)
                             ->samePropertyAs('fullnspath', 'fnp')
                        )
             )
             ->back('first');
        $this->prepareQuery();

        // class x { a $a; function foo() { $this->a = C::D()}}
        $this->atomIs('Propertydefinition')
             ->inIs('PPP')
             ->is('typehint', 'one')
             ->outIs('TYPEHINT')
             ->atomIsNot('Void')
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')

             ->outIs('DEFAULT')
             ->atomIs(self::FUNCTIONS_CALLS)
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')

             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs(self::CIT)
                     ->goToAllImplements(self::INCLUDE_SELF)
                     ->samePropertyAs('fullnspath', 'fnp')
             )
             ->back('first');
        $this->prepareQuery();

        // class x { false|int a $a = 'string';}
        $this->atomIs('Propertydefinition')
             ->inIs('PPP')
             ->as('ppp')
             ->is('typehint', array('or', 'and'))
             ->not(
                 $this->side()
                      ->outIs('TYPEHINT')
                      ->atomIs('Void')
             )

             ->back('first') // This avoids ending on another property definition when in multiple PPP definition
             ->outIs('DEFAULT')
             ->atomIs(array('Integer', 'Float', 'Null', 'Boolean', 'Arrayliteral', 'String', 'New', 'Functioncall'), self::WITH_CONSTANTS)
             ->savePropertyAs('label', 'type')
             // For New values
             ->optional(
                $this->side()
                     ->atomIs('New')
                     ->outIs('NEW')
                     ->savePropertyAs('fullnspath', 'fqn')
             )
             // For false values
             ->optional(
                $this->side()
                     ->atomIs('Boolean')
                     ->savePropertyAs('fullnspath', 'fqn2')
             )
             // For returntypes
             ->optional(
                $this->side()
                     ->atomIs('Functioncall')
                     ->inIs('DEFINITION')
                     ->outIs('RETURNTYPE') // This wil only works with single return types
                     ->savePropertyAs('fullnspath', 'fqn3')
             )
             ->back('ppp')

             ->notCompatibleWithType('type')
             ->back('first');

        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class UnknownParameterName extends Analyzer {
    protected $phpVersion = '8.0+';

    public function dependsOn(): array {
        return array('Complete/PropagateCalls',
                    );
    }

    public function analyze(): void {
        // function foo($a, $b)
        // foo(a: 1, c:2)
        $this->atomIs(self::FUNCTIONS_ALL)
             ->collectArguments('args', 'fullcode')
             ->outIs('DEFINITION')
             ->outIs('ARGUMENT')
             ->has('rankName')
             ->raw('filter{ !(it.get().value("rankName") in args);}');
        $this->prepareQuery();

        // function foo($a, $b)
        // foo(...[a: 1, c:2])
        $this->atomIs(self::FUNCTIONS_ALL)
             ->collectArguments('args', 'fullcode')
             ->outIs('DEFINITION')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->is('variadic', true)
             ->not(
                $this->side()
                     ->outIs('ARGUMENT')
                     ->atomIsNot(array('Keyvalue', 'Void'))
             )
             ->collectKeys('index', 'noDelimiter')
             ->raw('filter{ index.collect{"\\$" + it;}.minus(args) != [];}');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Typehints;

use Exakat\Analyzer\Analyzer;

class MissingReturntype extends Analyzer {
    public function dependsOn(): array {
        return array('Typehints/CouldBeNull',
                     'Typehints/CouldBeString',
                     'Typehints/CouldBeFloat',
                     'Typehints/CouldBeInt',
                     'Typehints/CouldBeBoolean',
                     'Typehints/CouldBeArray',
                     'Typehints/CouldBeCIT',
                    );
    }

    public function analyze(): void {
        // function foo() : string { return shell_exec('ls -hla'); }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')          // absence of returntype
             ->fullnspathIsNot('\\void')  // void as return type
             ->back('first')

             ->collectTypehints('returntypes')
             ->raw(<<<'GREMLIN'
or(
    __.filter{!('\\string'  in returntypes); }.in("ANALYZED").has("analyzer", 'Typehints/CouldBeString'),
    __.filter{!('\\int'     in returntypes); }.in("ANALYZED").has("analyzer", 'Typehints/CouldBeInt'),
    __.filter{!('\\null'    in returntypes); }.in("ANALYZED").has("analyzer", 'Typehints/CouldBeNull'),
    __.filter{!('\\float'   in returntypes); }.in("ANALYZED").has("analyzer", 'Typehints/CouldBeFloat'),
    __.filter{!('\\bool'    in returntypes); }.in("ANALYZED").has("analyzer", 'Typehints/CouldBeBoolean'),
    __.filter{!('\\array'   in returntypes); }.in("ANALYZED").has("analyzer", 'Typehints/CouldBeArray'),
    __.not(where(__.out('RETURNTYPE').hasLabel("Identifier", "Nsname", "Self", "Static", "Parent"))).in("ANALYZED").has("analyzer", 'Typehints/CouldBeCIT')
)
GREMLIN
)

             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class NonStaticMethodsCalledStatic extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/SetClassMethodRemoteDefinition',
                     'Complete/SetArrayClassDefinition',
                     'Variables/IsLocalConstant',
                    );
    }

    public function analyze(): void {
        // check outside the class : the first found class has not method
        // Here, we find methods that are in the grand parents, and not static.

        // the method is outside a class
        $this->atomIs('Staticmethodcall')
             ->hasNoClassTrait()

             ->inIs('DEFINITION')
             ->isNot('static', true)
             ->back('first');
        $this->prepareQuery();

        // the method is defined in a child class
        $this->atomIs('Staticmethodcall')
             ->goToClass()
             ->savePropertyAs('fullnspath', 'fnqOrigin')
             ->back('first')

             ->outIs('CLASS')
             ->atomIsNot(self::RELATIVE_CLASS)

             // The child must be below the parent, then it is an external class
             ->filter(
                $this->side()
                     ->inIs('DEFINITION')
                     ->goToAllParents(self::EXCLUDE_SELF)
                     ->samePropertyAs('fullnspath', 'fnqOrigin')
             )
             ->back('first')

             ->inIs('DEFINITION')
             ->isNot('static', true)
             ->back('first');
        $this->prepareQuery();

        // the method is defined in a parent class or self
        $this->atomIs('Staticmethodcall')
             ->goToClass()
             ->savePropertyAs('fullnspath', 'fnqOrigin')
             ->back('first')

             ->outIs('CLASS')
             ->atomIsNot(self::RELATIVE_CLASS)

             // The child must be below the parent, then it is an external class
             ->not(
                $this->side()
                     ->inIs('DEFINITION')
                     ->goToAllParents(self::INCLUDE_SELF)
                     ->samePropertyAs('fullnspath', 'fnqOrigin')
             )
             ->back('first')

             ->inIs('DEFINITION')
             ->isNot('static', true)

             ->inIs(array('METHOD', 'MAGICMETHOD'))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->not(
                $this->side()
                     ->goToClass()
                     ->goToAllParents(self::EXCLUDE_SELF)
                     ->samePropertyAs('fullnspath', 'fnp')
             );
        $this->prepareQuery();

        // ['a', 'm']() ; class a { function m() {}}
        $this->atomIs('Functioncall')
             ->outIs('NAME')
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->outWithRank('ARGUMENT', 0)
             ->atomIs(array('Staticclass', 'String'), self::WITH_CONSTANTS)
             ->inIs('ARGUMENT')
             ->inIs('DEFINITION')
             ->atomIs(array('Method', 'Magicmethod'))
             ->isNot('static', true)
             ->back('first');
        $this->prepareQuery();

        // 'a::m'() ; class a { function m() {}}
        $this->atomIs('Functioncall')
             ->outIs('NAME')
             ->atomIs('String', self::WITH_CONSTANTS)
             ->inIs('DEFINITION')
             ->atomIs(array('Method', 'Magicmethod'))
             ->isNot('static', true)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Structures;

use Exakat\Analyzer\Analyzer;

class ForeachReferenceIsNotModified extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/MakeFunctioncallWithReference',
                    );
    }

    public function analyze(): void {
        // case of a variable
        // foreach($a as &$b) { $c += $b; } // $b is not modified
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->outIsIE('RIGHT')
             ->is('reference', true)
             ->atomIs('Variable')
             ->savePropertyAs('code', 'name')
             ->inIs('VALUE')
             ->outIs('BLOCK')
             ->not(
                $this->side()
                     ->atomInsideNoDefinition(self::VARIABLES_USER)
                     ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
                     ->inIsIE(array('VARIABLE', 'OBJECT'))
                     ->is('isModified', true)
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Php;

use Exakat\Analyzer\Analyzer;

class AssignAnd extends Analyzer {
    public function dependsOn(): array {
        return array('Functions/KillsApp',
                    );
    }

    public function analyze(): void {
        // $a = $b and $c ; is actually ($a = $b) and $c; (lost and $c)
        $this->atomIs('Logical')
             ->codeIs(array('and', 'or', 'xor'))
             ->outIs('LEFT')
             ->atomIs('Assignation')
             ->back('first')
             ->outIs('RIGHT')
             ->atomIsNot('Exit')
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->atomIs('Functioncall')
                             ->inIs('DEFINITION')
                             ->analyzerIs('Functions/KillsApp')
                     )
             )
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class CallbackNeedsReturn extends Analyzer {
    public function dependsOn(): array {
        return array('Variables/IsLocalConstant',
                     'Complete/SetArrayClassDefinition',
                     'Complete/PropagateConstants',
                    );
    }

    public function analyze(): void {
        $ini = $this->loadIni('php_with_callback.ini');

        // Excluding some functions that don't REQUIRE return
        $ini->functions0 = array_diff($ini->functions0,
                                      array('\forward_static_call_array',
                                            '\forward_static_call',
                                            '\register_shutdown_function',
                                            '\register_tick_function',
                                            '\spl_autoload_register',
                                            )
                                      );

        $returningFunctions = $this->methods->getFunctionsByReturn();
        $voidReturningFunctions = array_merge($returningFunctions['void'],
                                              array_map(function ($x) { return trim($x, '\\');}, $returningFunctions['void'])
                                             );

        foreach($ini as $position => $functions) {
            $rank = substr($position, 9);
            if ($rank[0] === '_') {
                list(, $rank) = explode('_', $position);
            }

            //Any callback style : string, array, closure...
            $this->atomFunctionIs($functions)
                 ->outWithRank('ARGUMENT', $rank)
                 ->atomIs(array('Closure', 'String', 'Arrayliteral', 'Arrowfunction', 'Concatenation'), self::WITH_CONSTANTS)
                 ->optional(
                    $this->side()
                         ->inIs('DEFINITION')
                 )
                 ->not(
                    $this->side()
                         ->outIs('TYPEHINT')
                         ->fullnspathIs('\\void')
                 )
                ->not(
                    $this->side()
                         ->filter(
                            $this->side()
                                 ->outIs(array('ARGUMENT', 'USE'))
                                 ->is('reference', true)
                         )
                 )
                 ->not(
                    $this->side()
                         ->filter(
                            $this->side()
                                 ->outIs('ARGUMENT')
                                 ->is('reference', true)
                         )
                 )
                 ->atomIs(self::FUNCTIONS_ALL)
                 ->outIs('BLOCK')
                 ->noAtomInside('Return')
                 ->back('first');
            $this->prepareQuery();

            //the callback declares void as return type
            $this->atomFunctionIs($functions)
                 ->outWithRank('ARGUMENT', $rank)
                 // Could be : string, array, closure, arrow-function,
                 ->inIs('DEFINITION')
                 ->outIs('TYPEHINT')
                 ->fullnspathIs('\\void')
                 ->back('first');
            $this->prepareQuery();

            //the callback declares void as return type
            $this->atomFunctionIs($functions)
                 ->outWithRank('ARGUMENT', $rank)
                 ->atomIs('String', self::WITH_CONSTANTS)
                 ->noDelimiterIs($voidReturningFunctions, self::CASE_INSENSITIVE);
            $this->prepareQuery();

            //Normal class callback
            // Still needs DEFINITION link
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Functions;

use Exakat\Analyzer\Analyzer;

class DynamicCode extends Analyzer {
    public function analyze(): void {
        // function foo($x) { eval($x); echo $y; }
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('BLOCK')
             ->atomInsideNoDefinition(array('Eval', 'Include', 'Functioncall'))
             ->fullnspathIs(array('\\eval', '\\include', '\\include_once', '\\require', '\\require_once', '\\extract', ))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Variables;

use Exakat\Analyzer\Analyzer;

class SelfTransform extends Analyzer {
    public function analyze(): void {
        // $x = strtolower($x);
        // $x = A.$x.$b;
        // First step : marks variables in the right part
        $this->atomIs('Assignation')
             ->outIs('LEFT')
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'left')
             ->back('first')

             ->outIs('RIGHT')
             ->atomInsideNoDefinition(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'left');
        $this->prepareQuery();

        // Second step : marks variables in the left part
        $this->atomIs('Assignation')
             ->outIs('LEFT')
             ->atomIs(self::CONTAINERS)
             ->savePropertyAs('fullcode', 'left')
             ->as('results')
             ->back('first')

             ->outIs('RIGHT')
             ->atomInsideNoDefinition(self::CONTAINERS)
             ->samePropertyAs('fullcode', 'left')
             ->back('results');
        $this->prepareQuery();

        // short assignement case
        $this->atomIs('Assignation')
             ->codeIs(array('.=', '+=', '-=', '%=', '&&=', '&=', '*=', '**=', '/='))
             ->outIs('LEFT')
             ->atomIs(self::CONTAINERS);
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Variables;

use Exakat\Analyzer\Analyzer;

class Blind extends Analyzer {
    public function analyze(): void {
        $blinds = array('Variable', 'Staticproperty', 'Member', 'Array');

// foreach($source as $blind)
        $this->atomIs('Foreach')
             ->outIs(array('INDEX', 'VALUE'))
             ->atomIs($blinds);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Classes;

use Exakat\Analyzer\Analyzer;

class ExtendsStdclass extends Analyzer {
    public function analyze(): void {
        // class x extends \\stdclass
        $this->atomIs(self::CLASSES_ALL)
             ->extending(array('\\stdclass'))
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;

class None extends Reports {
    public const FILE_EXTENSION = '';
    public const FILE_FILENAME  = 'no_report';

    public function generate(string $folder, string $name = ''): string {
        display('Generating the empty format. ');

        return '';
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;


class Perfile extends Reports {
    public const FILE_EXTENSION = 'txt';
    public const FILE_FILENAME  = self::STDOUT;

    public function _generate(array $analyzerList): string {
        $analysisResults = $this->dump->fetchAnalysers($analyzerList);

        $perfile       = array();
        $titleCache    = array();
        $maxLine       = 0;
        $maxTitle      = 0;
        foreach($analysisResults->toArray() as $row) {
            if ($row['line'] === -1) { continue; }
            $this->count();
            if (!isset($titleCache[$row['analyzer']])) {
                $titleCache[$row['analyzer']] = $this->docs->getDocs($row['analyzer'], 'name');
            }

            $maxLine = max($maxLine, $row['line']);
            $maxTitle = max($maxTitle, strlen($titleCache[$row['analyzer']]), strlen($row['file']));
            if (strlen($titleCache[$row['analyzer']]) > 40) {
                $title = substr($titleCache[$row['analyzer']], 0, 37) . '...';
            } else {
                $title = $titleCache[$row['analyzer']];
            }
            $perfile[$row['file']][] = sprintf(' % 4s %-40s %-40s', $row['line'], $title, $row['fullcode']);
            $this->count();
        }

        $text = '';
        $line = strlen((string) $maxLine) + $maxTitle + 10;

        foreach($perfile as &$file) {
            sort($file);
        }
        unset($file);

        // sort by path
        ksort($perfile);

        foreach($perfile as $file => $issues) {
            $text .= str_repeat('-', $line) . "\n" .
                     " line  $file\n" .
                     str_repeat('-', $line) . "\n" .
                     implode("\n", $issues) . "\n" .
                     str_repeat('-', $line) . "\n"

                     . "\n"
                     . "\n";
        }

        return $text;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;


class Text extends Reports {
    public const FILE_EXTENSION = 'txt';
    public const FILE_FILENAME  = self::STDOUT;

    public function _generate(array $analyzerList): string {
        $analysisResults = $this->dump->fetchAnalysers($analyzerList);

        $results = array();
        $titleCache = array();
        $severityCache = array();
        foreach($analysisResults->toArray() as $row) {
            if (!isset($results[$row['file']])) {
                $file = array('errors'   => 0,
                              'warnings' => 0,
                              'fixable'  => 0,
                              'filename' => $row['file'],
                              'messages' => array());
                $results[$row['file']] = $file;
            }

            if (!isset($titleCache[$row['analyzer']])) {
                $titleCache[$row['analyzer']]    = $this->docs->getDocs($row['analyzer'], 'name');
                $severityCache[$row['analyzer']] = $this->docs->getDocs($row['analyzer'], 'severity');
            }

            $message = array('type'     => 'warning',
                             'source'   => $row['analyzer'],
                             'severity' => $severityCache[$row['analyzer']],
                             'fixable'  => 'fixable',
                             'message'  => $titleCache[$row['analyzer']],
                             'fullcode' => $row['fullcode']);

            if (!isset($results[ $row['file'] ]['messages'][ $row['line'] ])) {
                $results[ $row['file'] ]['messages'][ $row['line'] ] = array(0 => array());
            }
            $results[ $row['file'] ]['messages'][ $row['line'] ][0][] = $message;

            ++$results[ $row['file'] ]['warnings'];
        }

        $text = '';
        foreach($results as $file) {
            foreach($file['messages'] as $line => $column) {
                $messages = $column[0];
                foreach($messages as $message) {
                    $text .= $file['filename'] . ':' . $line . "\t" . $message['source'] . "\t" . $message['message'] . "\t" . $message['fullcode'] . "\n";
                    $this->count();
                }
            }
        }

        return $text;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;

use Exakat\Exakat;

use Bartlett\Sarif\Definition\ArtifactLocation;
use Bartlett\Sarif\Definition\PropertyBag;
use Bartlett\Sarif\Definition\ReportingConfiguration;
use Bartlett\Sarif\Definition\Location;
use Bartlett\Sarif\Definition\PhysicalLocation;
use Bartlett\Sarif\Definition\Region;
use Bartlett\Sarif\Definition\Message;
use Bartlett\Sarif\Definition\MultiformatMessageString;
use Bartlett\Sarif\Definition\ReportingDescriptor;
use Bartlett\Sarif\Definition\Result;
use Bartlett\Sarif\Definition\Run;
use Bartlett\Sarif\Definition\Tool;
use Bartlett\Sarif\Definition\ToolComponent;
use Bartlett\Sarif\SarifLog;

class Sarif extends Reports {
    public const FILE_EXTENSION = 'sarif';
    public const FILE_FILENAME  = 'exakat';

    private array $analyzers        = array();

    public function _generate(array $analyzerList): string {
        $driver = new ToolComponent('Exakat');
        $driver->setInformationUri('https://www.exakat.io/');
        $driver->setFullName('Exakat ' . Exakat::VERSION);
        $driver->setSemanticVersion(Exakat::VERSION);
        $driver->setVersion(Exakat::VERSION);

        $tool = new Tool($driver);
        $run = new Run($tool);

        $precisionCache   = array();
        $severityCache    = array();
        $descriptionCache = array();

        $analysisResults = $this->dump->fetchAnalysers($analyzerList);
        foreach($analysisResults->toArray() as $row) {
            // $sarif->addRule($row['analyzer'], $titleCache[$row['analyzer']], $descriptionCache[$row['analyzer']], $severityCache[$row['analyzer']], $precisionCache[$row['analyzer']]);
            if (!isset($titleCache[$row['analyzer']])) {
                $titleCache[$row['analyzer']]       = $this->docs->getDocs($row['analyzer'], 'name');
                $descriptionCache[$row['analyzer']] = $this->docs->getDocs($row['analyzer'], 'description');
                $severityCache[$row['analyzer']]    = $this->docs->getDocs($row['analyzer'], 'severity');
                $precisionCache[$row['analyzer']]   = $this->docs->getDocs($row['analyzer'], 'precision');
            }
            $this->count();

            if (!isset($this->analyzers[$row['analyzer']])) {
                $rule = new ReportingDescriptor($row['analyzer']);
                $rule->setShortDescription(
                    new MultiformatMessageString( $titleCache[$row['analyzer']] )
                );
                $rule->setHelp(
                    new MultiformatMessageString( $descriptionCache[$row['analyzer']] )
                );

                $propertyBag = new PropertyBag();
                $propertyBag->addProperty('precision', $precisionCache[$row['analyzer']]);
                $rule->setProperties($propertyBag);

                $reportingConf = new ReportingConfiguration();
                $propertyBag = new PropertyBag();
                $propertyBag->addProperty('level', $this->severity2level($severityCache[$row['analyzer']]));
                $reportingConf->setParameters($propertyBag);
                $rule->setDefaultConfiguration( $reportingConf );
                // @todo Help, HelpUri
                $driver->addRules(array($rule));

                $this->analyzers[$row['analyzer']] = count($this->analyzers);
            }

            $result = new Result(new Message((string) $row['fullcode'], md5((string) $row['fullcode'])));
            $result->setRuleId($row['analyzer']);
            $result->setRuleIndex($this->analyzers[$row['analyzer']]);
            $result->setLevel($this->severity2level($severityCache[$row['analyzer']]));
            $result->addPartialFingerprints(array('primaryLocationLineHash' => $this->fingerprints($row['file'], $row['line'], $row['fullcode'])));

            $location = new Location();
            $artifactLocation = new ArtifactLocation();
            $artifactLocation->setUri(ltrim($row['file'], '/'));
            $physicalLocation = new PhysicalLocation($artifactLocation);
            $region = new Region();
            $region->setStartLine($row['line']);
            $region->setEndLine($row['line']);
            $region->setStartColumn(1);
            $region->setEndColumn(200);
            $physicalLocation->setRegion($region);
            $location->setPhysicalLocation($physicalLocation);
            $result->addLocations(array($location));

            $run->addResults(array($result));

            $this->count();
        }

        $log = new SarifLog(array($run));

        return (string) $log;
    }

    private function severity2level(string $severity): string {
        // levels : none, note, warning, error
        switch($severity) {
            case 'Critical':
            case 'Major':
                $level = 'error';
                break;

            case 'Minor':
                $level = 'warning';
                break;

            default:
            case 'Note':
            case 'None':
                $level = 'note';
                break;

        }

        return $level;
    }

    private function fingerprints($fileName, $line, $fullcode): string {
        return sha1($fileName . ':' . $line . ':' . $fullcode);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;

use Exakat\Dump\Dump;
use Exakat\Exceptions\NoSuchReport;

abstract class Reports {
    public const STDOUT = 'stdout';
    public const INLINE = 'inline';

    public const NOT_RUN      = 'Not Run';
    public const YES          = 'Yes';
    public const NO           = 'No';
    public const INCOMPATIBLE = 'Incompatible';

    public static $FORMATS        = array('Ambassador', 'Ambassadornomenu', 'Drillinstructor', 'Top10', 'Diplomat',
                                          'Text', 'Xml', 'Uml', 'Yaml', 'Plantuml', 'None', 'Simplehtml', 'Owasp', 'Perfile', 'Beautycanon',
                                          'Phpconfiguration', 'Phpcompilation', 'Favorites', 'Manual',
                                          'Inventories', 'Clustergrammer', 'Filedependencies', 'Filedependencieshtml', 'Classdependencies', 'Stubs', 'StubsJson',
                                          'Radwellcode', 'Grade', 'Scrutinizer', 'Codesniffer', 'Phpcsfixer',
                                          'Facetedjson', 'Json', 'Onepagejson', 'Marmelab', 'Simpletable', 'Exakatyaml',
                                          'Codeflower', 'Dependencywheel', 'Phpcity', 'Sarb',
                                          'Exakatvendors', 'Topology',
                                          'Migration73', 'Migration74', 'Migration80', 'Migration81', 'Migration82',
                                          'Meters', 'Perrule',
                                          'CompatibilityPHP56', 'CompatibilityPHP74', 'CompatibilityPHP80', 'CompatibilityPHP81',
                                          'Compatibility',
                                          'Unused',
                                          //'DailyTodo',
                                          );

    protected $themesToShow = array('CompatibilityPHP56', //'CompatibilityPHP53', 'CompatibilityPHP54', 'CompatibilityPHP55',
                                    'CompatibilityPHP70', 'CompatibilityPHP71', 'CompatibilityPHP72', 'CompatibilityPHP73',
                                    'CompatibilityPHP74',
                                    'CompatibilityPHP80',
                                    'CompatibilityPHP81',
                                    'Dead code', 'Security', 'Analyze', 'Inventories',
                                    'Dump',
                                    );

    private $count = 0;

    protected $themesList = '';      // cache for themes list in SQLITE
    protected $config     = null;
    protected $docs       = null;

    protected $dump       = null;

    protected $datastore = null;
    protected $rulesets  = null;

    protected $options   = array();

    public function __construct() {
        $this->config    = exakat('config');
        $format = explode('\\', get_class($this));
        $format = array_pop($format);
        // warning, this is case sensitive. Options should not be.
        // warning, options for reports (and others) are simply based on the report name, and may end in conflict with other names
        $this->options   = $this->config->$format ?? array();
        $this->docs      = exakat('docs');

        if (file_exists($this->config->dump)) {
            $this->dump      = Dump::factory($this->config->dump);

            $this->rulesets  = exakat('rulesets');

            // Default analyzers
            $analyzers = array_merge($this->rulesets->getRulesetsAnalyzers($this->config->project_results ?? array()),
                                     array_keys($this->config->rulesets));
            $this->themesList = makeList($analyzers);
        }
    }

    protected function _generate(array $analyzerList): string {
        return '';
    }

    public static function getReportClass(string $report): string {
        $report = ucfirst(strtolower($report));
        return "\\Exakat\\Reports\\$report";
    }

    public function generate(string $folder, string $name= 'table'): string {
        if (empty($name)) {
            // FILE_FILENAME is defined in the children class
            $name = $this::FILE_FILENAME;
        }

        $rulesets = $this->config->project_rulesets;

        if (!empty($rulesets)) {
            if ($missing = $this->checkMissingRulesets()) {
                print "Can't produce " . static::class . ' format. There are ' . count($missing) . ' missing rulesets : ' . implode(', ', $missing) . ".\n";
                return '';
            }

            $list = $this->rulesets->getRulesetsAnalyzers($rulesets);
        } elseif (!empty($this->config->program)) {
            $list = makeArray($this->config->program);
        } else {
            $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);
        }

        $final = $this->_generate($list);

        if ($name === self::STDOUT) {
            echo $final;
            return '';
        } elseif ($name === self::INLINE) {
            return $final ;
        } else {
            file_put_contents("$folder/$name." . $this::FILE_EXTENSION, $final);
            return '';
        }
    }

    protected function count(int $step = 1): void {
        $this->count += $step;
    }

    public function getCount(): int {
        return $this->count;
    }

    public function dependsOnAnalysis(): array {
        if (empty($this->config->rulesets)) {
            return array();
        } else {
            return $this->config->rulesets;
        }
    }

    public function checkMissingRulesets(): array {
        $required = $this->dependsOnAnalysis();

        if (empty($required)) {
            return $required;
        }

        $available = $this->dump->fetchTable('themas')->toList('thema');

        if (empty($available)) {
            // Nothing found.
            return $required;
        }

        return array_diff($required, $available);
    }

    public static function getInstance(string $report): self {
        $class = "\Exakat\Reports\\$report";

        if (!class_exists($class)) {
            throw new NoSuchReport($class);
        }

        return new $class();
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Reports;


class Phpcompilation extends Reports {
    public const FILE_EXTENSION = 'txt';
    public const FILE_FILENAME  = 'compilePHP';

    protected function _generate(array $analyzerList): string {
        $themed = $this->rulesets->getRulesetsAnalyzers(array('Appinfo'));
        $res = $this->dump->fetchAnalysersCounts($themed);
        $sources = array_filter($res->toHash('analyzer', 'count'), function (int $x): bool { return $x > -1;});
        $this->count($res->getCount());

        $configureDirectives = json_decode(file_get_contents("{$this->config->dir_root}/data/configure.json"));

        // preparing the list of PHP extensions to compile PHP with
        $return = array(<<<'TEXT'
;;;;;;;;;;;;;;;;;;;;;;;;
; PHP configure list   ;
;;;;;;;;;;;;;;;;;;;;;;;;

TEXT
        ,
        './configure');
        $pecl = array();
        foreach($configureDirectives as $configure) {
            if (isset($sources[$configure->analysis])) {
                if(!empty($configure->activate) && $sources[$configure->analysis] != 0) {
                    $return[] = ' ' . $configure->activate;
                    if (!empty($configure->others)) {
                        $return[] = '   ' . implode(PHP_EOL . '    ', $configure->others);
                    }
                    if (!empty($configure->pecl)) {
                        $pecl[] = '#pecl install ' . basename($configure->pecl) . ' (' . $configure->pecl . ')';
                    }
                } elseif(!empty($configure->deactivate) && $sources[$configure->analysis] == 0) {
                    $return[] = ' ' . $configure->deactivate;
                }
            }
        }

        $return = array_merge($return, array(
                   '',
                   '; For debug purposes',
                   ';--enable-dtrace',
                   ';--disable-phpdbg',
                   '',
                   ';--enable-zend-signals',
                   ';--disable-opcache',
            ));

        $final = '';
        if (!empty($pecl)) {
            $c = count($pecl);
            $final .= '# install ' . ( $c === 1 ? 'one' : $c) . ' extra extension' . ($c === 1 ? '' : 's') . "\n";
            $final .= implode("\n", $pecl) . "\n\n";
        }
        $final .= implode("\n", $return);

        return $final;
    }

    public function dependsOnAnalysis(): array {
        return array('Appinfo',
                     );
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;


class Diplomat extends Emissary {
    public const FILE_FILENAME  = 'diplomat';
    public const FILE_EXTENSION = '';
    public const CONFIG_YAML    = 'Diplomat';

    public const TOPLIMIT = 10;
    public const LIMITGRAPHE = 40;

    protected $compatibilities = array();

    public function __construct() {
        parent::__construct();

        foreach(array('74', '80', '81') as $shortVersion) {
            $this->compatibilities[$shortVersion] = "Compatibility PHP $shortVersion[0].$shortVersion[1]";
        }

        $this->themesToShow = array('Analyze');
    }

    public function dependsOnAnalysis(): array {
        return array('CompatibilityPHP74', 'CompatibilityPHP80', 'CompatibilityPHP81',
                     'Appinfo',
                     'Analyze',
                     );
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Reports;

use Exakat\Analyzer\Analyzer;
use Exakat\Reports\Helpers\Highchart;
use Exakat\Reports\Helpers\Section;
use Exakat\Config;
use Exakat\Exakat;
use Exakat\Phpexec;
use Exakat\Vcs\Vcs;
use Symfony\Component\Yaml\Yaml as Symfony_Yaml;
use Exakat\Configsource\DatastoreConfig;
use Exakat\Tasks\Helpers\BaselineStash;

class Emissary extends Reports {
    public const FILE_FILENAME  = 'emissary';
    public const FILE_EXTENSION = '';
    public const CONFIG_YAML    = 'Emissary';

    protected $projectPath     = null;
    protected $finalName       = null;
    private $tmpName           = '';

    protected $frequences        = array();
    protected $timesToFix        = array();
    protected $themesForAnalyzer = array();
    protected $severities        = array();

    protected $generations       = array();
    protected $generations_files = array();

    protected $usedFiles         = array();

    private $baseHTML = '';

    public const TOPLIMIT = 10;
    public const LIMITGRAPHE = 40;

    private $inventories = array('constants'      => 'Constants',
                                 'classes'        => 'Classes',
                                 'interfaces'     => 'Interfaces',
                                 'functions'      => 'Functions',
                                 'traits'         => 'Traits',
                                 'namespaces'     => 'Namespaces',
                                 'Type/Url'       => 'URL',
                                 'Type/Regex'     => 'Regular Expr.',
                                 'Type/Sql'       => 'SQL',
                                 'Type/Email'     => 'Email',
                                 'Type/GPCIndex'  => 'Incoming variables',
                                 'Type/Md5string' => 'MD5 string',
                                 'Type/Mime'      => 'Mime types',
                                 'Type/Pack'      => 'Pack format',
                                 'Type/Printf'    => 'Printf format',
                                 'Type/Path'      => 'Paths',
                                 );

    protected $compatibilities = array();

    public function __construct() {
        parent::__construct();

        foreach(Config::PHP_VERSIONS as $shortVersion) {
            $this->compatibilities[$shortVersion] = "Compatibility PHP $shortVersion[0].$shortVersion[1]";
        }

        if ($this->rulesets !== null ){
            $this->frequences        = $this->rulesets->getFrequences();
            $this->timesToFix        = $this->rulesets->getTimesToFix();
            $this->themesForAnalyzer = $this->rulesets->getRulesetsForAnalyzer();
            $this->severities        = $this->rulesets->getSeverities();
        }
    }

    protected function makeMenu(): string {
        $menuYaml = Symfony_Yaml::parseFile(__DIR__ . '/' . static::CONFIG_YAML . '.yaml');

        $menu = array('<ul class="sidebar-menu">',
                      '<li class="header">&nbsp;</li>',
                      );
        foreach($menuYaml as $section) {
            $menu[] = $this->makeMenuHtml($section);
        }

        $menu[] = '</ul>';

        return implode(PHP_EOL, $menu);
    }

    protected function makeMenuHtml(array $sections): string {
        if (isset($sections['file'])) {
            $icon = $sections['icon'] ?? 'sticky-note-o';
            $menuTitle = $sections['menu'] ?? $sections['title'];

            $menu = "<li><a href=\"$sections[file].html\"><i class=\"fa fa-$icon\"></i> <span>$menuTitle</span></a></li>";
            if (isset($sections['method'])) {
                $this->generations[] = new Section($sections);
            } else {
                $this->generations_files[] = $sections['file'];
            }
        } elseif (isset($sections['subsections'])) {
            $icon      = $sections['icon'] ?? 'sticky-note-o';
            $menuTitle = $sections['menu'] ?? $sections['title'];

            $menu = array('<li class="treeview">',
                          "<a href=\"#\"><i class=\"fa fa-$icon\"></i> <span>$menuTitle</span><i class=\"fa fa-angle-left pull-right\"></i></a>",
                          '<ul class="treeview-menu">',
                          );

            foreach($sections['subsections'] as $subsection) {
                $menu[] = $this->makeMenuHtml($subsection);
            }

            $menu[] = '</ul>';
            $menu[] = '</li>';

            $menu = implode(PHP_EOL . '  ', $menu);
        } else {
            $menu = '';
        }

        return $menu;
    }

    private function initBasePage(): void {
        $baseHTML = file_get_contents("{$this->config->dir_root}/media/devfaceted/data/base.html") ?? '';

        $baseHTML = $this->injectBloc($baseHTML, 'EXAKAT_VERSION', Exakat::VERSION);
        $baseHTML = $this->injectBloc($baseHTML, 'EXAKAT_BUILD', (string) Exakat::BUILD);
        $project_name = $this->config->project_name;
        if (empty($project_name)) {
            $project_name = 'E';
        }
        $baseHTML = $this->injectBloc($baseHTML, 'PROJECT', $project_name);
        $baseHTML = $this->injectBloc($baseHTML, 'PROJECT_NAME', $project_name);
        $baseHTML = $this->injectBloc($baseHTML, 'PROJECT_LETTER', strtoupper($project_name[0]));

        $menu = $this->makeMenu();
        $inventories = array();
        foreach($this->inventories as $fileName => $title) {
            if (strpos($fileName, '/') === false) {
                $inventory_name = $fileName;
            } else {
                $total = $this->dump->fetchAnalysersCounts(array($fileName))->toInt();
                if ($total < 1) {
                    continue;
                }
                $inventory_name = strtolower(basename($fileName));
            }
            $inventories []= "              <li><a href=\"inventories_$inventory_name.html\"><i class=\"fa fa-circle-o\"></i>$title</a></li>\n";
        }

        $rulesets = $this->dump->fetchTable('themas');
        $rulesets->filter(function (array $x): bool { return substr($x['thema'], 0, 13) === 'Compatibility';});
        $compatibilities = array_map(function (string $x): string { $v = substr($x, -2); return "              <li><a href=\"compatibility_php$v.html\"><i class=\"fa fa-circle-o\"></i>{$this->compatibilities[$v]}</a></li>\n";},
                                     $rulesets->getColumn('thema'));

        $menu = $this->injectBloc($menu, 'INVENTORIES', implode(PHP_EOL, $inventories));
        $menu = $this->injectBloc($menu, 'COMPATIBILITIES', implode(PHP_EOL, $compatibilities));
        $this->baseHTML = $this->injectBloc($baseHTML, 'SIDEBARMENU', $menu);
    }

    protected function getBasedPage(string $file = ''): string {
        if (!file_exists("{$this->config->dir_root}/media/devfaceted/data/$file.html")) {
            display("Missing template file '$file' for " . static::class);

            return '';
        }

        $subPageHTML = file_get_contents("{$this->config->dir_root}/media/devfaceted/data/$file.html");
        $combinePageHTML = $this->injectBloc($this->baseHTML, 'BLOC-MAIN', $subPageHTML);

        return $combinePageHTML;
    }

    protected function putBasedPage(string $file, string $html): void {
        if (strpos($html, '{{BLOC-JS}}') !== false) {
            $html = str_replace('{{BLOC-JS}}', '', $html);
        }
        $html = str_replace('{{TITLE}}', "PHP Static analysis for {$this->config->project_name}", $html);

        file_put_contents("$this->tmpName/data/$file.html", $html);

        $this->usedFiles[] = "$file.html";
    }

    protected function injectBloc(string $html, string $bloc, string $content): string {
        return str_replace('{{' . $bloc . '}}', $content, $html);
    }

    public function generate(string $folder, string $name = self::FILE_FILENAME): string {
        if ($name === self::STDOUT) {
            print "Can't produce Emissary format to stdout\n";

            return '';
        }

        if ($missing = $this->checkMissingRulesets()) {
            print "Can't produce Emissary format. There are " . count($missing) . ' missing rulesets : ' . implode(', ', $missing) . ".\n";

            return '';
        }

        $this->finalName = "$folder/$name";
        $this->tmpName   = "$folder/.$name";

        $this->projectPath = $folder;

        $this->initFolder();
        $this->initBasePage();

        foreach($this->generations as $generation) {
            $method = $generation->method;
            if (!method_exists($this, $method)) {
                print "Warning : no such method as $method; Skipping\n";
                continue;
            }
            $this->$method($generation);
        }

        foreach($this->generations_files as $file) {
            $baseHTML = $this->getBasedPage($file);
            $this->putBasedPage($file, $baseHTML);
        }

        $this->cleanFolder();

        return '';
    }

    protected function initFolder(): void {
        // Clean temporary destination
        if (file_exists($this->tmpName)) {
            rmdirRecursive($this->tmpName);
        }

        // Copy template
        if (!copyDir("{$this->config->dir_root}/media/devfaceted", $this->tmpName)) {
            print "Error while preparing the folder. A copy failed\n";
            return;
        }
    }

    protected function cleanFolder(): void {
        if (file_exists("{$this->tmpName}/data/base.html")) {
            unlink("{$this->tmpName}/data/base.html");
            unlink("{$this->tmpName}/data/menu.html");
            unlink("{$this->tmpName}/data/empty.html");
        }

        $files = glob("{$this->tmpName}/data/*.html");
        $files = array_map('basename', $files);
        foreach(array_diff($files, $this->usedFiles) as $file) {
            unlink("{$this->tmpName}/data/$file");
        }

        // Clean final destination
        if ($this->finalName !== '/') {
            rmdirRecursive($this->finalName);
        }

        if (file_exists($this->finalName)) {
            display("{$this->finalName} folder was not cleaned. Please, remove it before producing the report. Aborting report\n");
            return;
        }

        rename($this->tmpName, $this->finalName);
    }

    protected function setPHPBlocs(string $description): string {
        $description = preg_replace_callback("#<\?php(.*?)\n\?>#is", function (array $x): string {
            $return = '<pre style="border: 1px solid #ddd; background-color: #f5f5f5;">&lt;?php ' . PHP_EOL . PHPSyntax($x[1]) . '?&gt;</pre>';
            return $return;
        }, $description);

        return $description;
    }

    protected function generateDocumentation(Section $section): void {
        $analyzersList = array_merge($this->rulesets->getRulesetsAnalyzers($this->dependsOnAnalysis()));
        $analyzersList = array_unique($analyzersList);

        $baseHTML = $this->getBasedPage($section->source);
        $docHTML = array();

        foreach($analyzersList as $analyzerName) {
            $analyzer = $this->rulesets->getInstance($analyzerName, null, $this->config);
            assert ($analyzer instanceof Analyzer, "Could not get an analyzer for $analyzerName in the documentation\n");

            $description = $this->docs->getDocs($analyzerName);
            assert(isset($description['name'], $description['description']), "Could not get a name or description for $analyzerName in the documentation\n");

            $analyzersDocHTML = '<h2><a href="issues.html#analyzer=' . $this->toId($analyzerName) . '" id="' . $this->toId($analyzerName) . '">' . $description['name'] . '</a></h2>';

            $badges = array();
            $exakatSince = $description['exakatSince'] ?? '';
            if(!empty($exakatSince)){
                $badges[] = "[Since $exakatSince]";
            }

            $badges[] = '[ -P ' . $analyzer->getInBaseName() . ' ]';
            if (isset($description['name'])) {
                $badges[] = '[ <a href="https://exakat.readthedocs.io/en/latest/Rules.html#' . $this->toOnlineId($description['name']) . '">Online docs</a> ]';
            }

            $versionCompatibility = $description['phpversion'];
            if ($versionCompatibility !== Analyzer::PHP_VERSION_ANY) {
                if (strpos($versionCompatibility, '+') !== false) {
                    $versionCompatibility = substr($versionCompatibility, 0, -1) . ' and more recent ';
                } elseif (strpos($versionCompatibility, '-') !== false) {
                    $versionCompatibility = ' older than ' . substr($versionCompatibility, 0, -1);
                }
                $badges[] = '[ PHP ' . $versionCompatibility . ']';
            }

            $analyzersDocHTML .= '<p>' . implode(' - ', $badges) . '</p>';

            if (!empty($description['seeAlso'][0])) {
                $last = count($description['seeAlso']) - 1;
                $seeAlso[$last] = 'and ' . $description['seeAlso'][$last] . '.';
                $description = $description['description'] . "\nSee also " . implode(', ', $description['seeAlso']);
            } else {
                $description = $description['description'];
            }

            static $regex;
            if (empty($regex)) {
                $php_native_functions = parse_ini_file("{$this->config->dir_root}/data/php_functions.ini")['functions'];
                usort($php_native_functions, function (string $a, string $b): int { return strlen($b) <=> strlen($a);} );
                $regex = '/(' . implode('|', $php_native_functions) . ')\(\)/m';
            }
            $description = preg_replace($regex, '`\1() <https://www.php.net/\1>`_', $description);

            $analyzersDocHTML .= '<p>' . nl2br($this->setPHPBlocs($description)) . '</p>';
            $analyzersDocHTML  = rst2quote($analyzersDocHTML);
            $analyzersDocHTML  = rst2htmlLink($analyzersDocHTML);
            $analyzersDocHTML  = rst2literal($analyzersDocHTML);
            $analyzersDocHTML  = rsttable2html($analyzersDocHTML);
            $analyzersDocHTML  = rstlist2html($analyzersDocHTML);

            $clearphp = $description['clearphp'] ?? '';
            if(!empty($clearphp)){
                $analyzersDocHTML.='<p>This rule is named <a target="_blank" href="https://github.com/dseguy/clearPHP/blob/master/rules/' . $clearphp . '.md">' . $clearphp . '</a>, in the clearPHP reference.</p>';
            }
            $docHTML[] = $analyzersDocHTML;
        }

        $finalHTML = $this->injectBloc($baseHTML, 'BLOC-ANALYZERS', implode(PHP_EOL, $docHTML));
        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS', '<script src="scripts/highlight.pack.js"></script>');
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateFavorites(Section $section): void {
        $baseHTML = $this->getBasedPage($section->source);

        $favorites = new Favorites();
        $favoritesRules = $this->getTopFile($this->rulesets->getRulesetsAnalyzers(array('Favorites')));
        $favoritesList = json_decode($favorites->generate($favoritesRules, self::INLINE));

        $html = array();
        $highchart = new Highchart();

        foreach(array_keys((array) $favoritesList) as $analyzer) {
            $analyzerList = $this->dump->fetchHashAnalyzer($analyzer)->toHash('key', 'value');

            $table = array();
            $values = array();
            $name = $this->docs->getDocs($analyzer, 'name');

            $total = 0;
            foreach($analyzerList as $key => $value) {
                $table []= '
                <div class="clearfix">
                   <div class="block-cell">' . makeHtml($key) . '</div>
                   <div class="block-cell text-center">' . $value . '</div>
                 </div>
';
                if ($value > 0) {
                    $values[] = array('label' => $key,
                                      'value' => (int) $value);
                }
                $total += $value;
            }

            if (($repeat = 4 - count($analyzerList)) > 0) {
                $table []= str_repeat('
                <div class="clearfix">
                   <div class="block-cell">&nbsp;</div>
                   <div class="block-cell text-center">&nbsp;</div>
                 </div>
', $repeat );
            }
            $table = implode('', $table);

            // Ignore if we have no occurrences
            if ($total === 0) {
                continue;
            }

            $html[] = <<<HTML
            <div class="col-md-3">
              <div class="box">
                <div class="box-header with-border">
                  <h3 class="box-title"><a href="favorites_issues.html#analyzer=$analyzer" title="$name">$name</a></h3>
                </div>
                <div class="box-body chart-responsive">
                  <div id="donut-chart_$name"></div>
                  <div class="clearfix">
                    <div class="block-cell bold">Number</div>
                    <div class="block-cell bold text-center">Count</div>
                  </div>
                  $table
                </div>
                <!-- /.box-body -->
              </div>
            </div>
HTML;
            if (count($html) % 5 === 0) {
                $html[] = '          </div>
          <div class="row">';
            }

            $highchart->addDonut('donut-chart_' . $name,  $values);
        }

        $donut = (string) $highchart;

        $html = '<div class="row">' . implode(PHP_EOL, $html) . '</div>';

        $baseHTML = $this->injectBloc($baseHTML, 'FAVORITES', $html);
        $baseHTML = $this->injectBloc($baseHTML, 'BLOC-JS', $donut);
        $baseHTML = $this->injectBloc($baseHTML, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $baseHTML);
    }

    protected function generateDashboard(Section $section): void {
        $baseHTML = $this->getBasedPage($section->source);

        $tags = array();
        $code = array();

        // Bloc top left
        $hashData = $this->getHashData();
        $finalHTML = $this->injectBloc($baseHTML, 'BLOCHASHDATA', $hashData);

        // bloc Issues
        $issues = $this->getIssuesBreakdown();
        $finalHTML = $this->injectBloc($finalHTML, 'BLOCISSUES', $issues['html']);

        // bloc severity
        $severity = $this->getSeverityBreakdown();
        $finalHTML = $this->injectBloc($finalHTML, 'BLOCSEVERITY', $severity['html']);

        // Marking the audit date
        $this->makeAuditDate($finalHTML);

        // top 10
        $fileHTML = $this->getTopFile($this->rulesets->getRulesetsAnalyzers($this->themesToShow));
        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $fileHTML);
        $analyzerHTML = $this->getTopAnalyzers($this->rulesets->getRulesetsAnalyzers($this->themesToShow));
        $finalHTML = $this->injectBloc($finalHTML, 'TOPANALYZER', $analyzerHTML);

        $highchart = new Highchart();

        $highchart->addDonut('donut-chart_issues',  $issues['script']);
        $highchart->addDonut('donut-chart_severity', $severity['script']);

        $fileOverview = $this->getFileOverview();
        $highchart->addSeries('filename',
                              $fileOverview['scriptDataFiles'],
                              array('name' => 'Critical', 'data' => $fileOverview['scriptDataCritical']),
                              array('name' => 'Major',    'data' => $fileOverview['scriptDataMajor']),
                              array('name' => 'Minor',    'data' => $fileOverview['scriptDataMinor']),
                              array('name' => 'None',     'data' => $fileOverview['scriptDataNone'])
                              );

        $analyzerOverview = $this->getAnalyzerOverview();
        $highchart->addSeries('container',
                              $analyzerOverview['scriptDataAnalyzer'],
                              array('name' => 'Critical', 'data' => $analyzerOverview['scriptDataAnalyzerCritical']),
                              array('name' => 'Major',    'data' => $analyzerOverview['scriptDataAnalyzerMajor']),
                              array('name' => 'Minor',    'data' => $analyzerOverview['scriptDataAnalyzerMinor']),
                              array('name' => 'None',     'data' => $analyzerOverview['scriptDataAnalyzerNone'])
                              );

        $blocjs = (string) $highchart;

        $blocjs = str_replace($tags, $code, $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateCounts(Section $section, string $hash, string $suffix = '', string $name = ''): void {
        $finalHTML = $this->getBasedPage($section->source);

        // List of extensions used
        $res = $this->dump->fetchHashResults($hash);
        if ($res->isEmpty()) {
            $this->emptyResult($section);

            return;
        }

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            $data[$value['key'] . $suffix] = $value['value'];

            $html [(int) $value['value'] ]= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }
        krsort($html);
        $html = implode('', $html);

        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $html);

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => $name, 'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateClassDesignations(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        $html = array('<table class="table table-striped">',
                      '<tr><td>Namespace</td><td>Class / interface</td><td>Count</td><td>Fitting typehints</td><td>Count</td><td>As typehint</td></tr>',
                       );

        $namespaces = $this->dump->fetchTable('namespaces')->toHash('id', 'namespace');

        // il faut constuire les différentes possibilitées avant de construire le tableau,
        // afin de suivre les extends, et tous les rassembler.

        $res = $this->dump->fetchTable('cit');
        $parents = array();
        $children = array();
        $names = array();
        foreach($res->toArray() as $row) {
            if (empty($row['extends'])) {
                $parents[$row['id']] = array();
            } else {
                array_collect_by($parents, $row['id'],(intval($row['extends']) > 0 ? $row['extends'] : 'class ' . $row['extends']));
                array_collect_by($children, ( (int) $row['extends'] > 0 ? $row['extends'] : 'class ' . $row['extends']), $row['id']);
            }
            $names[$row['id']] = $row['type'] . ' ' . $namespaces[$row['namespaceId']] . $row['name'];
        }

        $res_implements = $this->dump->fetchTable('cit_implements')->toArray();
        $implements = array();
        foreach($res_implements as $row) {
            array_collect_by($implements, $row['implementing'], $row);
            array_collect_by($parents, $row['implementing'], intval($row['implements']) > 0 ? $row['implements'] : 'interface ' . $row['implements']);
            array_collect_by($children, intval($row['implements']) > 0 ? $row['implements'] : 'interface ' . $row['implements'], $row['implementing']);
        }

        /// Collect classes and interfaces that accept a class as typehint : class C extends B {} => C => [C, B]
        do {
            $parents2 = array();
            foreach($parents as $key => $aieux) {
                $cleaned = array(array());
                foreach($aieux as $id => $aieul) {
                    if (isset($parents[$aieul])) {
                        $cleaned[] = $parents[$aieul];
                        $cleaned[] = array($aieul);
                    } else {
                        $cleaned[] = array($aieul);
                    }
                }

                $parents2[$key] = array_values(array_unique(array_merge(...$cleaned)));
            }

            $toPropagate = count($parents2, 1) - count($parents, 1);
            $parents = $parents2;
        } while($toPropagate > 0);

        /// Collect classes that can fit a class used as typehint : class C extends B {} => B => [C, B]
        // children classes may fit when the parent is used as typehint. Interface don't count as result
        do {
            $children2 = array();
            foreach($children as $key => $child) {
                $cleaned = array();
                foreach($child as $id => $kid) {
                    if (isset($children[$kid])) {
                        $cleaned[] = $children[$kid];
                    }
                    $cleaned[] = array($kid);
                }
                $children2[$key] = array_values(array_unique(array_merge(...$cleaned)));
            }

            $toPropagate = count($children2, 1) - count($children, 1);
            $children = $children2;
        } while($toPropagate > 0);

        foreach($res->toArray() as $row) {
            $td = array();
            $td[] = '<td style="vertical-align: top">' . $namespaces[$row['namespaceId']] . '</td>';
            $td[] = '<td style="vertical-align: top">' . $row['type'] . ' ' . $row['name'] . '</td>';

            // fitting typehint
            $list = array();
            if ($row['type'] == 'class') {
                $list[] = $names[$row['id']] ?? $row['id'];
            }
            if (isset($parents[$row['id']])) {
                foreach($parents[$row['id']] as $higher) {
                    $list[] = $names[$higher] ?? $higher;
                }
            }
            sort($list);
            if (empty($list)) {
                $td[] = '<td>0</td>';
                $td[] = '<td>&nbsp;</td>';
            } else {
                $td[] = '<td style="vertical-align: top">' . count($list) . '</td>';
                $td[] = '<td><ul><li>' . implode('</li><li>', $list) . '</li></ul></td>';
            }

            // when used as typehint
            $list = array();
            if ($row['type'] == 'class') {
                $list[] = $names[$row['id']] ?? $row['id'];
            }
            if (isset($children[$row['id']])) {
                foreach($children[$row['id']] as $higher) {
                    $n = $names[$higher] ?? $higher;
                    if ($n[0] === 'c') {
                        $list[] = $n;
                    }
                }
            }
            sort($list);
            if (empty($list)) {
                $td[] = '<td>0</td>';
                $td[] = '<td>&nbsp;</td>';
            } else {
                $td[] = '<td style="vertical-align: top">' . count($list) . '</td>';
                $td[] = '<td><ul><li>' . implode('</li><li>', $list) . '</li></ul></td>';
            }

            $html[] = '<tr>' . join('', $td) . '</tr>';
        }

        $html[] = '</table>';

        $finalHTML = $this->injectBloc($finalHTML, 'DESCRIPTION', '');
        $finalHTML = $this->injectBloc($finalHTML, 'CONTENT', implode(PHP_EOL, $html));
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateLocalVariableCounts(Section $section): void {
        $this->generateCounts($section, 'Local Variable Counts', ' var.', 'Local variables');
    }

    protected function generateParameterCounts(Section $section): void {
        $this->generateCounts($section, 'ParameterCounts', ' param.', 'Parameters');
    }

    protected function generatePropertyCounts(Section $section): void {
        $this->generateCounts($section, 'CIT property counts', ' prop.', 'Properties');
    }

    protected function generateMethodCounts(Section $section): void {
        $this->generateCounts($section, 'CIT method counts', ' method.', 'Methods');
    }

    protected function generateClassConstantCounts(Section $section): void {
        $this->generateCounts($section, 'CIT class constant counts', ' const.', 'Class Constant');
    }

    protected function generateTailoredRuleset(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);
        $res = $this->dump->fetchAnalysersCounts($list);
        $rulesets = array('[ruleset_name]');
        foreach($res->toArray() as $r) {
            $rulesets[] = "analyzer[] = \"$r[analyzer]\";";
        }

        $rulesets = implode(PHP_EOL, $rulesets);

        $finalHTML = $this->injectBloc($finalHTML, 'RULESET',  $rulesets);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateExtensionsBreakdown(Section $section): void {
        // List of extensions used
        $extensionList = $this->dump->getExtensionList();

        $html = array();
        $data = array();
        foreach ($extensionList->toArray() as $value) {
            $shortName = str_replace('Extensions/Ext', 'ext/', $value['analyzer']);
            $data[$value['analyzer']] = $value['count'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $shortName . '</div>
                      <div class="block-cell-issue text-center">' . $value['count'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Extensions', $data, $html);
    }

    protected function generatePHPFunctionBreakdown(Section $section): void {
        // List of php functions used
        $res = $this->dump->fetchTable('phpStructures');
        $res->filter(function (array $x): bool { return $x['type'] === 'function'; });
        $res->order(function (array $a, array $b): int { return $b['count'] <=> $a['count']; });

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            $data[$value['name']] = $value['count'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['name'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['count'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'PHP Native Functions', $data, $html);
    }

    protected function generateClassTypehints(Section $section): void {
        // List of typehints used
        $res = $this->dump->fetchHashResults('Typehinting stats');
        $res->order(function (array $a, array $b): int { return $b['value'] <=> $a['value']; });

        $html = array();
        $data = array();
        $omit = array(  'totalArguments',
                        'allTotal',
                        'totalFunctions',
                        'methodTotal',
                        'withTypehint',
                        'allWithTypehint',
                        'methodWithTypehint',
                        'functionTotal',
                        'scalartype',
                        'functionWithTypehint2',
                        'closureTotal',
                        'argNullable',
                        'allWithReturnTypehint',
                        'interfaceTypehint',
                        'classTypehint',
                        'functionWithReturnTypehint',
                        'returnNullable',
                        'methodWithReturnTypehint',
                        'withReturnTypehint',
                        'closureWithTypehint',
                        'closureWithReturnTypehint',
                        'arrowfunctionTotal',
                        'arrowfunctionWithTypehint',
                        'arrowfunctionWithReturnTypehint',
                        'totalProperties',
                        'typedProperties',
                        'multipleTypehints',
                        'functionWithTypehint',
        );

        foreach ($res->toArray() as $value) {
            if (in_array($value['key'], $omit, \STRICT_COMPARISON)) { continue; }
            $data[$value['key']] = $value['value'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Class typehint usage', $data, $html);
    }

    protected function generatePHPConstantsBreakdown(Section $section): void {
        // List of php constant used
        $res = $this->dump->fetchTable('phpStructures');
        $res->filter(function (array $x): bool { return $x['type'] === 'constant'; });
        $res->order(function (array $a, array $b): int { return $b['count'] <=> $a['count']; });

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            $data[$value['name']] = $value['count'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['name'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['count'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Constants', $data, $html);
    }

    protected function generatePHPClassesBreakdown(Section $section): void {
        // List of php functions used
        $res = $this->dump->fetchTable('phpStructures');
        $res->filter(function (array $x): bool { return in_array($x['type'], array('class', 'interface', 'trait'), \STRICT_COMPARISON); });
        $res->order(function (array $a, array $b): int { return $b['count'] <=> $a['count']; });

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            $data[$value['name']] = $value['count'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['name'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['count'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Classes', $data, $html);
    }

    protected function generateGraphList(string $filename, string $title, string $data_name, array $data, string $html): void {
        $finalHTML = $this->getBasedPage('extension_list');
        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $html);

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => $data_name, 'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $title);

        $this->putBasedPage($filename, $finalHTML);
    }

    public function getHashData(): string {
        $info = array(
            'Number of PHP files'                   => $this->dump->fetchHash('files')->toString(),
            'Number of lines of code'               => $this->dump->fetchHash('loc')->toString(),
            'Number of lines of code with comments' => $this->dump->fetchHash('locTotal')->toString(),
            'PHP used'                              => $this->dump->fetchHash('php_version')->toString(),
        );

        // fichier
        $totalFile = $this->dump->fetchHash('files')->toString();
        $totalFileAnalysed = count($this->getFilesWithResults());
        $totalFileSansError = $totalFile - $totalFileAnalysed;
        if ((int) $totalFile === 0) {
            $percentFile = 100;
        } else {
            $percentFile = abs(round($totalFileSansError / $totalFile * 100));
        }

        // analyzer
        list($totalAnalyzerUsed, $totalAnalyzerReporting) = array_values($this->getTotalAnalyzer());
        $totalAnalyzerWithoutError = $totalAnalyzerUsed - $totalAnalyzerReporting;
        if ($totalAnalyzerUsed > 0) {
            $percentAnalyzer = abs(round($totalAnalyzerWithoutError / $totalAnalyzerUsed * 100));
        } else {
            $percentAnalyzer = 100;
        }

        $html = <<<HTML
    <div class="box">
        <div class="box-header with-border">
            <h3 class="box-title">Project Overview</h3>
        </div>
    
        <div class="box-body chart-responsive">
            <div class="row">
                <div class="sub-div">
                    <p class="title"><span># of PHP</span> files</p>
                    <p class="value">{$info['Number of PHP files']}</p>
                </div>
                <div class="sub-div">
                    <p class="title"><span>PHP</span> Used</p>
                    <p class="value">{$info['PHP used']}</p>
                 </div>
            </div>
            <div class="row">
                <div class="sub-div">
                    <p class="title"><span>PHP</span> LoC</p>
                    <p class="value">{$info['Number of lines of code']}</p>
                </div>
                <div class="sub-div">
                    <p class="title"><span>Total</span> LoC</p>
                    <p class="value">{$info['Number of lines of code with comments']}</p>
                </div>
            </div>
            <div class="row">
                <div class="sub-div">
                    <div class="title">Files free of issues (%)</div>
                    <div class="progress progress-sm">
                        <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: {$percentFile}%">
                            {$totalFileSansError}
                        </div><div style="color:black; text-align:center;">{$totalFileAnalysed}</div>
                    </div>
                    <div class="pourcentage">{$percentFile}%</div>
                </div>
                <div class="sub-div">
                    <div class="title">Analyzers free of issues (%)</div>
                    <div class="progress progress-sm active">
                        <div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: {$percentAnalyzer}%">
                            {$totalAnalyzerWithoutError}
                        </div><div style="color:black; text-align:center;">{$totalAnalyzerReporting}</div>
                    </div>
                    <div class="pourcentage">{$percentAnalyzer}%</div>
                </div>
            </div>
        </div>
    </div>
HTML;

        return $html;
    }

    public function getIssuesBreakdown(): array {
        $rulesets = array('Code Smells'  => 'Analyze',
                          'Dead Code'    => 'Dead code',
                          'Security'     => 'Security',
                          'Performances' => 'Performances');

        $data = array();
        foreach ($rulesets AS $key => $categorie) {
            $list = $this->rulesets->getRulesetsAnalyzers(array($categorie));
            $res = $this->dump->fetchAnalysersCounts($list);
            $res->filter(function (array $x): bool { return $x['count'] >= -1;});
            $counts = $res->getColumn('count');
            $data[] = array('label' => $key, 'value' => array_sum($counts));
        }

        // ordonné DESC par valeur
        uasort($data, function (array $a, array $b): int {
            return $b['value'] <=> $a['value'];
        });
        $issuesHtml = '';
        $dataScript = array();

        foreach ($data as $value) {
            $issuesHtml .= '<div class="clearfix">
                   <div class="block-cell">' . $value['label'] . '</div>
                   <div class="block-cell text-center">' . $value['value'] . '</div>
                 </div>';
            $dataScript[] = $value;
        }

        $nb = 4 - count($data);
        $filler = '<div class="clearfix">
                   <div class="block-cell">&nbsp;</div>
                   <div class="block-cell text-center">&nbsp;</div>
                 </div>';
        if ($nb > 0) {
            $issuesHtml .= str_repeat($filler, $nb);
        }

        return array('html'   => $issuesHtml,
                     'script' => $dataScript);
    }

    public function getSeverityBreakdown(): array {
        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);
        $res = $this->dump->getSeverityBreakdown($list);

        $html = array();
        $dataScript = array();
        foreach ($res->toArray() as $value) {
            $html []= <<<HTML
<div class="clearfix">
    <div class="block-cell">$value[label]</div>
    <div class="block-cell text-center">$value[value]</div>
</div>
HTML;
            $dataScript[] = $value;
        }

        if (($c = 4 - $res->getCount()) > 0) {
            $html []= str_repeat('<div class="clearfix">
                       <div class="block-cell">&nbsp;</div>
                       <div class="block-cell text-center">&nbsp;</div>
                     </div>', $c);
        }
        $html = implode('', $html);

        return array('html'   => $html,
                     'script' => $dataScript);
    }

    protected function getFilesWithResults(): array {
        $list = $this->rulesets->getRulesetsAnalyzers(array('Analyze'));

        return $this->dump->getFilesWithResults($list)->toArray();
    }

    protected function getTotalAnalysedFile(): int {
        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);

        return $this->dump->getAnalyzedFiles($list);
    }

    protected function generateAnalyzers(): void {
        $analysers = $this->getAnalyzersResultsCounts();

        $baseHTML = $this->getBasedPage('analyses');
        $analyserHTML = '';

        foreach ($analysers as $analyser) {
            $analyserHTML .= '<tr>';

            $analyserHTML.= '<td><a href="issues.html#analyzer=' . $this->toId($analyser['analyzer']) . '" title="' . $analyser['label'] . '">' . $analyser['label'] . '</a></td>
                        <td>' . $analyser['recipes'] . '</td>
                        <td>' . $analyser['issues'] . '</td>
                        <td>' . $analyser['files'] . '</td>
                        <td>' . $analyser['severity'] . '</td>
                        <td>' . $this->frequences[$analyser['analyzer']] . ' %</td>';
            $analyserHTML .= '</tr>';
        }

        $finalHTML = $this->injectBloc($baseHTML, 'BLOC-ANALYZERS', $analyserHTML);
        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS', '<script src="scripts/datatables.js"></script>');

        $this->putBasedPage('analyses', $finalHTML);
    }

    protected function getAnalyzersResultsCounts(): array {
        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);

        $result = $this->dump->getAnalyzersResultsCounts($list);

        $return = array();
        foreach ($result->toArray() as $row) {
            $row['label'] = $this->docs->getDocs($row['analyzer'], 'name');
            $row['recipes' ] =  implode(', ', $this->themesForAnalyzer[$row['analyzer']]);

            $return[] = $row;
        }

        return $return;
    }

    private function generateNoIssues(Section $section): void {
        $list = $this->rulesets->getRulesetsAnalyzers(array(
        'Analyze',
        'Security',
        'Performances',
        'CompatibilityPHP53',
        'CompatibilityPHP54',
        'CompatibilityPHP55',
        'CompatibilityPHP56',
        'CompatibilityPHP70',
        'CompatibilityPHP71',
        'CompatibilityPHP72',
        'CompatibilityPHP73',
        'CompatibilityPHP74',
        'CompatibilityPHP80',
        ));

        $result = $this->dump->fetchAnalysersCounts($list);
        $result->filter(function (array $x): bool { return substr($x['analyzer'], 0, 6) !== 'Common';});

        $baseHTML = $this->getBasedPage($section->source);

        $filesHTML = array();
        foreach ($result->toArray() as $row) {
            $analyzer = $this->rulesets->getInstance($row['analyzer'], null, $this->config);

            if ($analyzer === null) {
                continue;
            }

            $filesHTML []= '<tr><td>' . $this->makeDocLink($row['analyzer']) . '</td></tr>';
        }
        $filesHTML = implode(PHP_EOL, $filesHTML);

        $finalHTML = $this->injectBloc($baseHTML, 'BLOC-FILES', $filesHTML);
        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS', '<script src="scripts/datatables.js"></script>');
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateFiles(Section $section): void {
        $files = $this->getFilesResultsCounts();

        $baseHTML = $this->getBasedPage($section->source);
        $filesHTML = '';

        foreach ($files as $file) {
            $filesHTML.= '<tr>';


            $filesHTML.='<td> <a href="issues.html#file=' . $this->toId($file['file']) . '" title="' . $file['file'] . '">' . $file['file'] . '</a></td>
                        <td>' . $file['loc'] . '</td>
                        <td>' . $file['issues'] . '</td>
                        <td>' . $file['analyzers'] . '</td>';
            $filesHTML.= '</tr>';
        }

        $finalHTML = $this->injectBloc($baseHTML, 'BLOC-FILES', $filesHTML);
        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS', '<script src="scripts/datatables.js"></script>');
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function getFilesResultsCounts(): array {
        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);
        $res = $this->dump->getFilesResultsCounts($list)->toHash('file');

        return $res;
    }

    protected function getFilesCount(array $list = array(), int $limit = 10): array {
        $res = $this->dump->getFileBreakdown($list);

        return array_slice($res->toArray(), 0, $limit);
    }

    protected function getTopFile(array $list, string $file = 'issues'): string {
        $data = $this->getFilesCount($list, self::TOPLIMIT);

        $html = array();
        foreach ($data as $value) {
            $html []= '<div class="clearfix">
                    <a href="' . $file . '.html#file=' . $this->toId($value['file']) . '" title="' . $value['file'] . '">
                      <div class="block-cell-name">' . $value['file'] . '</div>
                    </a>
                    <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }

        $nb = 10 - count($data);
        if ($nb > 0) {
            $html []= str_repeat('<div class="clearfix">
                          <div class="block-cell-name">&nbsp;</div>
                          <div class="block-cell-issue text-center">&nbsp;</div>
                      </div>', $nb);
        }

        return implode(PHP_EOL, $html);
    }

    protected function getFileOverview(): array {
        $xAxis        = array();
        $dataMajor    = array();
        $dataCritical = array();
        $dataNone     = array();
        $dataMinor    = array();

        $severities = $this->getSeveritiesNumberBy('file');
        unset($severities['None']);
        uasort($severities, function (array $a, array $b): int { return array_sum($b) <=> array_sum($a); });
        $severities = array_slice($severities, 0, 10);

        foreach ($severities as $file => $value) {
            $xAxis[]        = $file;
            $dataCritical[] = $value['Critical'] ?? 0;
            $dataMajor[]    = $value['Major']    ?? 0;
            $dataMinor[]    = $value['Minor']    ?? 0;
            $dataNone[]     = $value['None']     ?? 0;
        }

        return array(
            'scriptDataFiles'    => $xAxis,
            'scriptDataCritical' => $dataCritical,
            'scriptDataMajor'    => $dataMajor,
            'scriptDataMinor'    => $dataMinor,
            'scriptDataNone'     => $dataNone,
        );
    }

    protected function getAnalyzersCount(int $limit): array {
        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);
        $res = $this->dump->getAnalyzersCount($list);

        return array_slice($res->toArray(), 0, $limit);
    }

    protected function getTopAnalyzers(array $list, string $file = 'issues'): string {
        $res = $this->dump->getTopAnalyzers($list, self::TOPLIMIT);

        $data = array();
        foreach ($res->toArray() as $row) {
            $data[] = array('label' => $this->docs->getDocs($row['analyzer'], 'name'),
                            'value' => $row['number'],
                            'name'  => $row['analyzer']);
        }

        $html = array();
        foreach ($data as $value) {
            $html []= '<div class="clearfix">
                    <a href="' . $file . '.html#analyzer=' . $this->toId($value['name']) . '" title="' . $value['label'] . '">
                      <div class="block-cell-name">' . $value['label'] . '</div> 
                    </a>
                    <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }

        $nb = 10 - count($data);
        if ($nb > 0) {
            $html []= str_repeat('<div class="clearfix">
                          <div class="block-cell-name">&nbsp;</div>
                          <div class="block-cell-issue text-center">&nbsp;</div>
                      </div>', $nb);
        }

        $html = implode(PHP_EOL, $html);

        return $html;
    }

    protected function getSeveritiesNumberBy(string $type = 'file'): array {
        $list = $this->rulesets->getRulesetsAnalyzers($this->themesToShow);

        $res = $this->dump->getSeveritiesNumberBy($list, $type);
        $return = array();
        foreach($res->toArray() as $value) {
            $return[$value[$type]][$value['severity']] = $value['count'];
        }

        return $return;
    }

    protected function getAnalyzerOverview(): array {
        $data = $this->getAnalyzersCount(self::TOPLIMIT);
        $xAxis        = array();
        $dataMajor    = array();
        $dataCritical = array();
        $dataNone     = array();
        $dataMinor    = array();

        $severities = $this->getSeveritiesNumberBy('analyzer');
        foreach ($data as $value) {
            $ini = $this->docs->getDocs($value['analyzer']);
            $xAxis[]        = $ini['name'];
            $dataCritical[] = empty($severities[$value['analyzer']]['Critical']) ? 0 : $severities[$value['analyzer']]['Critical'];
            $dataMajor[]    = empty($severities[$value['analyzer']]['Major']) ? 0 : $severities[$value['analyzer']]['Major'];
            $dataMinor[]    = empty($severities[$value['analyzer']]['Minor']) ? 0 : $severities[$value['analyzer']]['Minor'];
            $dataNone[]     = empty($severities[$value['analyzer']]['None']) ? 0 : $severities[$value['analyzer']]['None'];
        }

        return array(
            'scriptDataAnalyzer'         => $xAxis,
            'scriptDataAnalyzerCritical' => $dataCritical,
            'scriptDataAnalyzerMajor'    => $dataMajor,
            'scriptDataAnalyzerMinor'    => $dataMinor,
            'scriptDataAnalyzerNone'     => $dataNone,
        );
    }

    private function generateNewIssues(Section $section): void {
        $baselines = new BaselineStash($this->config);
        $previous = $baselines->getBaseline();

        if ($previous === BaselineStash::NO_BASELINE) {
            $this->emptyResult($section);

            return;
        }

        $issues = $this->getIssuesFaceted($this->rulesets->getRulesetsAnalyzers($this->themesToShow));

        $oldissues = $this->getNewIssuesFaceted($this->rulesets->getRulesetsAnalyzers($this->themesToShow), $previous);
        $diff = array_diff($issues, $oldissues);

        $this->generateIssuesEngine($section, $diff);
    }

    private function generateIssues(Section $section): void {
        $issues = $this->getIssuesFaceted($this->rulesets->getRulesetsAnalyzers($this->themesToShow));
        $this->generateIssuesEngine($section, $issues);
    }

    protected function generateIssuesEngine(Section $section, array $issues = array()): void {
        if (empty($issues)) {
            $issues = $this->getIssuesFaceted(makeArray($this->rulesets->getRulesetsAnalyzers(makeArray($section->ruleset))));
        }

        $total = count($issues);
        $issues = implode(', ' . PHP_EOL, $issues);
        $blocjs = <<<JAVASCRIPTCODE
        
  <script>
  "use strict";

    $(document).ready(function() {

      var data_items = [
$issues
];

      var item_template =  
        '<tr>' +
          '<td width="20%"><a href="<%= "analyses_doc.html#" + obj.analyzer_md5 %>" title="Documentation for <%= obj.analyzer %>"><i class="fa fa-book"></i></a> <%= obj.analyzer %></td>' +
          '<td width="20%"><a href="<%= "codes.html#file=" + obj.file + "&line=" + obj.line %>" title="Go to code"><%= obj.file + ":" + obj.line %></a></td>' +
          '<td width="18%"><%= obj.code %></td>' + 
          '<td width="2%"><%= obj.code_detail %></td>' +
          '<td width="7%" align="center"><%= obj.severity %></td>' +
          '<td width="7%" align="center"><%= obj.complexity %></td>' +
          '<td width="16%"><%= obj.recipe %></td>' +
        '</tr>' +
        '<tr class="fullcode">' +
          '<td colspan="7" width="100%"><div class="analyzer_help"><%= obj.analyzer_help %></div><pre><code><%= obj.code_plus %></code><div class="text-right"><a target="_BLANK" href="<%= "codes.html#file=" + obj.file + "&line=" + obj.line %>" class="btn btn-info">View File</a></div></pre></td>' +
        '</tr>';
      var settings = { 
        items           : data_items,
        facets          : { 
          'analyzer'  : 'Analysis',
          'file'      : 'File',
          'severity'  : 'Severity',
          'complexity': 'Time To Fix',
          'receipt'   : 'Rulesets'
        },
        facetContainer     : '<div class="facetsearch btn-group" id=<%= id %> ></div>',
        facetTitleTemplate : '<button class="facettitle multiselect dropdown-toggle btn btn-default" data-toggle="dropdown" title="None selected"><span class="multiselect-selected-text"><%= title %></span><b class="caret"></b></button>',
        facetListContainer : '<ul class="facetlist multiselect-container dropdown-menu" style="max-height: 450px; overflow: auto;"></ul>',
        listItemTemplate   : '<li class=facetitem id="<%= id %>" data-analyzer="<%= data_analyzer %>" data-file="<%= data_file %>"><span class="check"></span><%= name %><span class=facetitemcount>(<%= count %>)</span></li>',
        bottomContainer    : '<div class=bottomline></div>',  
        resultSelector   : '#results',
        facetSelector    : '#facets',
        resultTemplate   : item_template,
        paginationCount  : 50
      }   
      $.facetelize(settings);
      
      var analyzerParam = window.location.hash.split('analyzer=')[1];
      console.log(analyzerParam);
      var fileParam = window.location.hash.split('file=')[1];
      if(analyzerParam !== undefined) {
        $('#analyzer .facetlist').find("[data-analyzer='" + analyzerParam.toLowerCase() + "']").click();
      }
      if(fileParam !== undefined) {
        $('#file .facetlist').find("[data-file='" + fileParam.toLowerCase() + "']").click();
      }
    });
  </script>
JAVASCRIPTCODE;

        $baseHTML = $this->getBasedPage($section->source);
        $finalHTML = $this->injectBloc($baseHTML, 'BLOC-JS', $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'TOTAL', (string) $total);
        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function getIssuesFaceted(array $ruleset): array {
        return $this->getIssuesFacetedDb($ruleset);
    }

    public function getNewIssuesFaceted(array $ruleset, string $path): array {
        $sqlite = new \Sqlite3($path);
        $res = $sqlite->query('SELECT count(*) FROM sqlite_master WHERE type = "table" AND name != "sqlite_sequence";');

        if ($res === false || $res->fetchArray(\SQLITE3_NUM)[0] < 10) {
            return array();
        }

        $result = $this->dump->fetchTable('linediff');
        $linediff = array();
        foreach($result->toArray() as $row) {
            $linediff[$row['file']][$row['line']] = $row['diff'];
        }

        $oldIssues = $this->getIssuesFacetedDb($ruleset);
        foreach($oldIssues as &$issue) {
            $i = json_decode($issue);

            // Skip wrong lines, but why ?
            if (!($i instanceof \stdClass)) {
                continue;
            }

            if (isset($linediff[$i->file]) && $i->line > -1) {
                foreach($linediff[$i->file] as $line => $diff) {
                    if ($i->line > $line) {
                        $i->line += $diff;
                    }
                }
                if ($i->line > $line) {
                    $issue = json_encode($i);
                }
            }
        }
        unset($issue);

        return $oldIssues;
    }

    public function getIssuesFacetedDb(array $ruleset): array {
        $results = $this->dump->fetchAnalysers($ruleset);
        $results->filter(function (array $x): bool { return !in_array($x['fullcode'], array('Not Compatible With PHP Version', 'Not Compatible With PHP Configuration'), \STRICT_COMPARISON); });

        $TTFColors = array('Instant'  => '#5f492d',
                           'Quick'    => '#e8d568',
                           'Slow'     => '#d06960',
                           'None'     => '#89070b'
                           );

        $severityColors = array('Critical' => '#ff0000',   // red
                                'Major'    => '#FFA500',   // Orange
                                'Minor'    => '#BDB76B',   // darkkhaki
                                'None'     => '#D3D3D3',   // lightgrey
                                );

        $items = array();
        foreach($results->toArray() as $row) {
            $item = array();
            $ini = $this->docs->getDocs($row['analyzer']);
            $item['analyzer']       = $ini['name'];
            $item['analyzer_md5']   = $this->toId($row['analyzer']);
            $item['file' ]          = $row['line'] === -1 ? $this->config->project_name : $row['file'];
            $item['file_md5' ]      = $this->toId($row['file']);
            $item['code' ]          = PHPSyntax((string) $row['fullcode']);
            $item['code_detail']    = '<i class="fa fa-plus "></i>';
            $item['code_plus']      = PHPSyntax((string) $row['fullcode']);
            $item['link_file']      = $row['file'];
            $item['line' ]          = $row['line'];
            $item['severity']       = '<i class="fa fa-warning" style="color: ' . $severityColors[$this->severities[$row['analyzer']]] . '"></i>';
            $item['complexity']     = '<i class="fa fa-cog" style="color: ' . $TTFColors[$this->timesToFix[$row['analyzer']]] . '"></i>';
            $item['recipe' ]        =  implode(', ', $this->themesForAnalyzer[$row['analyzer']]);
            $lines                  = explode("\n", $ini['description']);
            $item['analyzer_help' ] = $lines[0];

            $items[] = json_encode($item);
            $this->count();
        }

        return $items;
    }

    private function getClassByType(string $type): string {
        if ($type === 'Critical' || $type === 'Long') {
            $class = 'text-orange';
        } elseif ($type === 'Major' || $type === 'Slow') {
            $class = 'text-red';
        } elseif ($type === 'Minor' || $type === 'Quick') {
            $class = 'text-yellow';
        }  elseif ($type === 'Note' || $type === 'Instant') {
            $class = 'text-blue';
        } else {
            $class = 'text-gray';
        }

        return $class;
    }

    protected function generateProcFiles(Section $section): void {
        $files = array();
        $fileList = $this->dump->fetchTable('files')->getColumn('file');
        foreach($fileList as $file) {
            $files []= "<tr><td>$file</td></tr>";
        }
        $files = implode(PHP_EOL, $files);

        $nonFiles = array();
        $ignoredFiles = $this->dump->fetchTable('ignoredFiles')->toArray();
        foreach($ignoredFiles as $row) {
            if (empty($row['file'])) { continue; }

            $nonFiles []= "<tr><td>{$row['file']}</td><td>{$row['reason']}</td></tr>";
        }
        $nonFiles = implode(PHP_EOL, $nonFiles);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'FILES', $files);
        $html = $this->injectBloc($html, 'NON-FILES', $nonFiles);
        $html = $this->injectBloc($html, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $html);
    }

    protected function generateAnalyzersList(Section $section): void {
        $analyzers = array();

        foreach($this->rulesets->getRulesetsAnalyzers($this->themesToShow) as $analyzer) {
            $analyzers []= '<tr><td>' . $this->docs->getDocs($analyzer, 'name') . '</td><td>' . $analyzer . "</td></tr>\n";
        }
        $analyzers = implode(PHP_EOL, $analyzers);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'ANALYZERS', $analyzers);
        $html = $this->injectBloc($html, 'TITLE',     $section->title);

        $this->putBasedPage($section->file, $html);
    }

    private function generateExternalLib(Section $section): void {
        $externallibraries = json_decode(file_get_contents("{$this->config->dir_root}/data/externallibraries.json"));

        $libraries = array();
        $externallibrariesList = $this->dump->fetchTable('externallibraries')->toArray();

        foreach($externallibrariesList as $row) {
            $name = strtolower($row['library']);
            $url  = $externallibraries->{$name}->homepage;
            $name = $externallibraries->{$name}->name;
            if (empty($url)) {
                $homepage = '';
            } else {
                $homepage = "<a href=\"$url\">$row[library]</a>";
            }
            $libraries []= "<tr><td>$name</td><td>$row[file]</td><td>$homepage</td></tr>";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'LIBRARIES', implode(PHP_EOL, $libraries));
        $html = $this->injectBloc($html, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $html);
    }

    protected function generateBugFixes(Section $section): void {
        $table = '';

        $bugfixes = exakat('methods')->getBugFixes();

        $results = $this->dump->fetchAnalysers(array('Php/MiddleVersion'));

        $rows = array();
        foreach($results->toArray() as $row) {
            $rows[strtolower(substr($row['fullcode'], 0, (int) strpos($row['fullcode'], '(')))] = $row;
        }

        foreach($bugfixes as $bugfix) {
            if (empty($bugfix['solvedIn73']) &&
                empty($bugfix['solvedIn72']) &&
                empty($bugfix['solvedIn71']) &&
                empty($bugfix['solvedIn70']) ) { continue; }

            if (!empty($bugfix['function'])) {
                if (!isset($rows[$bugfix['function']])) { continue; }

                $cve = $this->Bugfixes_cve($bugfix['cve'] ?? '');
                $table .= '<tr>
    <td>' . $bugfix['title'] . '</td>
    <td>' . ($bugfix['solvedIn73'] ? $bugfix['solvedIn73'] : '-') . '</td>
    <td>' . ($bugfix['solvedIn72'] ? $bugfix['solvedIn72'] : '-') . '</td>
    <td>' . ($bugfix['solvedIn71'] ? $bugfix['solvedIn71'] : '-') . '</td>
    <td>' . ($bugfix['solvedIn70'] ? $bugfix['solvedIn70'] : '-') . '</td>
    <td>' . ($bugfix['solvedInDev'] ? $bugfix['solvedInDev'] : '-') . '</td>
    <td><a href="https://bugs.php.net/bug.php?id=' . $bugfix['bugs'] . '">#' . $bugfix['bugs'] . '</a></td>
    <td>' . $cve . '</td>
                </tr>';
            } elseif (!empty($bugfix['analyzer'])) {
                $subanalyze = $this->dump->fetchAnalysersCounts(array($bugfix['analyzer']))->toString('count');

                $cve = $this->Bugfixes_cve($bugfix['cve']);

                if ($subanalyze === 0) { continue; }
                $table .= '<tr>
    <td>' . $bugfix['title'] . '</td>
    <td>' . ($bugfix['solvedIn73'] ? $bugfix['solvedIn73'] : '-') . '</td>
    <td>' . ($bugfix['solvedIn72'] ? $bugfix['solvedIn72'] : '-') . '</td>
    <td>' . ($bugfix['solvedIn71'] ? $bugfix['solvedIn71'] : '-') . '</td>
    <td>' . ($bugfix['solvedIn70'] ? $bugfix['solvedIn70'] : '-') . '</td>
    <td>' . ($bugfix['solvedInDev'] ? $bugfix['solvedInDev'] : '-') . '</td>
    <td><a href="https://bugs.php.net/bug.php?id=' . $bugfix['bugs'] . '">#' . $bugfix['bugs'] . '</a></td>
    <td>' . $cve . '</td>
                </tr>';
            } else {
                continue; // ignore. Possibly some mis-configuration
            }
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'BUG_FIXES', $table);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function generatePhpConfiguration(Section $section): void {
        $phpConfiguration = new Phpcompilation();
        $report = $phpConfiguration->generate('', self::INLINE);

        $configline = trim($report);
        $configline = str_replace(array(' ', "\n") , array('&nbsp;', "<br />\n", ), $configline);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'COMPILATION', $configline);
        $html = $this->injectBloc($html, 'TITLE', $section->title);

        $this->putBasedPage($section->source, $html);
    }

    protected function generateCompatibilityEstimate(Section $section): void {
        $html = $this->getBasedPage($section->source);

        $versions = Phpexec::VERSIONS;
        $scores = array_fill_keys(array_values($versions), 0);
        $versions = array_reverse($versions);

        $analyzers = (array) json_decode(file_get_contents($this->config->dir_root . '/data/compatibility.json'));

        $colors = array('59BF00', '59BF00', '59BF00', 'BEC500', 'CB6C00', 'D20700', 'D80064', 'DE00D7', '7900E5', '7900E5', '7900E5', '7900E5', );
        // This must be the same lenght than the list of versions

        $results = $this->dump->fetchAnalysersCounts(array_keys($analyzers));
        $counts = $results->toHash('analyzer', 'count');

        $data = array();
        $data2 = array();
        foreach($analyzers as $analyzer => $analyzerVersion) {
            $coeff = $analyzerVersion[-1] === '+' ? 1 : -1;

            foreach($versions as $version) {
                if (!isset($counts[$analyzer])) {
                    $data2[$analyzer][$version] = '<i class="fa fa-eye-slash" style="color: #dddddd"></i>';
                } elseif ($counts[$analyzer] === 0) {
                    $data2[$analyzer][$version] = '<i class="fa fa-eye-slash" style="color: #dddddd"></i>';
                } elseif ($coeff * version_compare($version, $analyzerVersion) >= 0) {
                    $data[$analyzer][$version] = '<i class="fa fa-check-square-o" style="color: seagreen"></i>';
                    ++$scores[$version];
                } else {
                    $data[$analyzer][$version] = '<i class="fa fa-warning" style="color: crimson"></i>';
                }
            }
        }

        $incompilable = array();
        foreach($versions as $version) {
            $shortVersion = $version[0] . $version[2];

            $res = $this->dump->fetchHash("notCompilable$shortVersion")->toString();
            if ($res === 'N/C') {
                $incompilable[$shortVersion] = '<i class="fa fa-eye-slash" style="color: #dddddd"></i>';
                continue;
            }

            $results = $this->dump->fetchTable("compilation$shortVersion");
            // -1 is for no result found.
            if ($results->getCount() <= 0) {
                $incompilable[$shortVersion] = '<i class="fa fa-check-square-o" style="color: seagreen"></i>';
            } else {
                $incompilable[$shortVersion] = '<i class="fa fa-warning" style="color: crimson"></i>';
            }
        }

        $table = array();
        $titles = '<tr><th>Version</th><th>Name</th><th>' . implode('</th><th>', array_keys(array_values($data2)[0]) ) . '</th></tr>';
        $table []= '<tr><td>&nbsp;</td><td>Compilation</td><td>' . implode('</td><td>', $incompilable) . "</td></tr>\n";
        $data = array_merge($data, $data2);
        foreach($data as $name => $row) {
            if (!class_exists("\Exakat\Analyzer\\" . str_replace('/', '\\', $name))) {
                continue;
            }

            $analyzer = $this->rulesets->getInstance($name, null, $this->config);
            if ($analyzer === null) {
                continue;
            }

            $link = '<a href="analyses_doc.html#' . $this->toId($name) . '" alt="Documentation for ' . $name . '"><i class="fa fa-book"></i></a>';

            $color = $colors[array_search(rtrim($analyzers[$name], '+-'), $versions)];
            $table []= "<tr><td style=\"background-color: #{$color};\">$analyzers[$name]</td><td>$link {$this->docs->getDocs($name, 'name')}</td><td>" . implode('</td><td>', $row) . "</td></tr>\n";
        }

        $table = implode('', $table);

        $theTable = <<<HTML
        					<table class="table table-striped">
        						<tr></tr>
        						$titles
        						$table
        					</table>
HTML;

        $max = max($scores);
        $key = array_keys($scores, $max);

        if ($max === count($data)) {
            $suggestion = 'This code is compatible with PHP ' . implode(', ', $key);
        } else {
            $suggestion = 'We have determined ' . count($key) . ' PHP version' . (count($key) > 1 ? 's' : '') . '. The compatible estimations are PHP ' . implode(', ', $key) . '. ';
        }

        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', $suggestion);
        $html = $this->injectBloc($html, 'CONTENT', $theTable);

        $this->putBasedPage($section->file, $html);
    }

    protected function generateAuditConfig(Section $section): void {
        $config = new DatastoreConfig();
        $ini  = $config->toIni();
        $yaml = $config->toYaml();

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'CONFIG_INI', $ini);
        $html = $this->injectBloc($html, 'CONFIG_YAML', $yaml);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function generateAnalyzerSettings(Section $section): void {
        $settings = '';

        $info = array(array('Code name', $this->config->project_name));
        if (!empty($this->config->project_description)) {
            $info[] = array('Code description', $this->config->project_description);
        }
        if (!empty($this->config->project_packagist)) {
            $info[] = array('Packagist', '<a href="https://packagist.org/packages/' . $this->config->project_packagist . '">' . $this->config->project_packagist . '</a>');
        }
        $info = array_merge($info, $this->getVCSInfo());

        $info[] = array('Number of PHP files', $this->dump->fetchHash('files')->toString());
        $info[] = array('Number of lines of code', $this->dump->fetchHash('loc')->toString());
        $info[] = array('Number of lines of code with comments', $this->dump->fetchHash('locTotal')->toString());

        $info[] = array('Analysis execution date', date('r', $this->dump->fetchHash('audit_end')->toInt()));
        $info[] = array('Analysis runtime', duration($this->dump->fetchHash('audit_end')->toString() - $this->dump->fetchHash('audit_start')->toString()));
        $info[] = array('Report production date', date('r', time()));

        $php = exakat('php');
        $info[] = array('PHP used', $this->config->phpversion . ' (' . $php->getConfiguration('phpversion') . ')');

        $info[] = array('Exakat version', $this->dump->fetchHash('exakat_version')->toString() . ' ( Build ' . $this->dump->fetchHash('exakat_build')->toString() . ') ');
        $list = $this->config->ext->getPharList();
        $html = array();
        foreach(array_keys($list) as $name) {
            $html[] = '<li>' . basename($name, '.phar') . '</li>';
        }
        $info[] = array('Exakat modules', '<ul>' . implode(PHP_EOL, $html) . '</ul>');

        foreach($info as &$row) {
            $row = '<tr><td>' . implode('</td><td>', $row) . '</td></tr>';
        }
        unset($row);

        $settings = implode(PHP_EOL, $info);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'SETTINGS', $settings);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateErrorMessages(Section $section): void {
        $errorMessages = '';

        $results = $this->dump->fetchAnalysers(array('Structures/ErrorMessages'));

        foreach($results->toArray() as $row) {
            $errorMessages .= "<tr><td>{$row['htmlcode']}</td><td>{$row['file']}</td><td>{$row['line']}</td></tr>\n";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'ERROR_MESSAGES', $errorMessages);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateExternalServices(Section $section): void {
        $externalServices = array();

        $res = $this->dump->fetchTable('configFiles')->toArray();
        foreach($res as $row) {
            if (empty($row['homepage'])) {
                $link = '';
            } else {
                $link = '<a href="' . $row['homepage'] . '">' . $row['homepage'] . '&nbsp;<i class="fa fa-sign-out"></i></a>';
            }

            $externalServices []= "<tr><td>$row[name]</td><td>$row[file]</td><td>$link</td></tr>";
        }
        $externalServices = implode(PHP_EOL, $externalServices);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'EXTERNAL_SERVICES', $externalServices);
        $html = $this->injectBloc($html, 'TTLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function generateDirectiveList(Section $section): void {
        // @todo automate this : Each string must be found in Report/Content/Directives/*.php and vice-versa
        $directives = array('standard', 'bcmath', 'date', 'file',
                            'fileupload', 'mail', 'ob', 'env',
                            // standard extensions
                            'apc', 'amqp', 'apache', 'assertion', 'curl', 'dba',
                            'filter', 'image', 'intl', 'ldap',
                            'mbstring',
                            'opcache', 'openssl', 'pcre', 'pdo', 'pgsql',
                            'session', 'sqlite', 'sqlite3',
                            // pecl extensions
                            'com', 'eaccelerator',
                            'geoip', 'ibase',
                            'imagick', 'mailparse', 'mongo',
                            'trader', 'wincache', 'xcache'
                             );

        $directiveList = '';
        // Possibly move this to a specific ruleset
        $list = $this->rulesets->getRulesetsAnalyzers(array('Appinfo'));
        $res = $this->dump->fetchAnalysersCounts($list);

        foreach($res->toArray() as $row) {
            $data = array();

            if ($row['analyzer'] === 'Structures/FileUploadUsage' && $row['count'] !== 0) {
                $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>File Upload</td></tr>\n";
                $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/fileupload.json"));
            } elseif ($row['analyzer'] === 'Php/UsesEnv' && $row['count'] !== 0) {
                $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>Environment</td></tr>\n";
                $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/env.json"));
            } elseif ($row['analyzer'] === 'Php/UseBrowscap' && $row['count'] !== 0) {
                $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>Browser</td></tr>\n";
                $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/browscap.json"));
            } elseif ($row['analyzer'] === 'Php/DlUsage' && $row['count'] === 0) {
                $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>Enable DL</td></tr>\n";
                $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/enable_dl.json"));
            } elseif ($row['analyzer'] === 'Php/ErrorLogUsage' && $row['count'] !== 0) {
                $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>Error Log</td></tr>\n";
                $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/errorlog.json"));
            } elseif ($row['analyzer'] === 'Security/CantDisableFunction' ||
                      $row['analyzer'] === 'Security/CantDisableClass'
                      ) {
                $list = $this->dump->getFunctionsFromAnalyzer('Security/CantDisableFunction');

                if (isset($disable)) {
                    continue;
                }
                $disable = parse_ini_file("{$this->config->dir_root}/data/disable_functions.ini");
                $suggestions = array_diff($disable['disable_functions'], $list);

                $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/disable_functions.json"));

                // disable_functions
                $data[0]->suggested = implode(', ', $suggestions);
                $data[0]->documentation .= "\n; " . count($list) . " sensitive functions were found in the code. Don't disable those : " . implode(', ', $list);

                $list = $this->dump->getFunctionsFromAnalyzer('Security/CantDisableClass');
                $suggestions = array_diff($disable['disable_classes'], $list);

                // disable_functions
                $data[1]->suggested = implode(',', $suggestions);
                $data[1]->documentation .= "\n; " . count($list) . " sensitive classes were found in the code. Don't disable those : " . implode(', ', $list);
                $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>Disable features</td></tr>\n";
            } elseif ($row['count'] !== 0) {
                $ext = substr($row['analyzer'], 14);
                if (in_array($ext, $directives, \STRICT_COMPARISON)) {
                    $data = json_decode(file_get_contents("{$this->config->dir_root}/data/directives/$ext.json"));
                    $directiveList .= "<tr><td colspan=3 bgcolor=#AAA>$ext</td></tr>\n";
                }
            }

            foreach($data as $directive) {
                $directiveList .= "<tr><td>{$directive->name}</td><td>{$directive->suggested}</td><td>{$directive->documentation}</td></tr>\n";
            }
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'DIRECTIVE_LIST', $directiveList);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function generateCompilations(Section $section): void {
        $compilations = array();

        $total = $this->dump->fetchHash('files')->toInt();

        foreach(array_unique(array_merge(array($this->config->phpversion[0] . $this->config->phpversion[2]), $this->config->other_php_versions)) as $suffix) {
            $suffix = (string) $suffix;
            $res = $this->dump->fetchHash("compilation$suffix");
            if ($res->getCount() === -1) {
                $version = $suffix[0] . '.' . $suffix[1];
                $compilations []= "<tr><td>$version</td><td>N/A</td><td>N/A</td><td>Compilation not tested</td><td>N/A</td></tr>";
                continue; // Table was not created
            }

            $res = $this->dump->fetchTable("compilation$suffix");
            $files = $res->getColumn('file');

            if (empty($files)) {
                $files       = 'No compilation error found.';
                $errors      = 'N/A';
                $total_error = 'N/A';
            } else {
                $readErrors = $res->getColumn('error');

                $errors      = array_count_values($readErrors);
                $errors      = array_keys($errors);
                $errors      = array_keys(array_count_values($errors));
                $errors      = '<ul><li>' . implode("</li>\n<li>", $errors) . '</li></ul>';

                $total_error = count($files) . ' (' . number_format(count($files) / $total * 100, 0) . '%)';
                $files       = array_keys(array_count_values($files));
                $files       = '<ul><li>' . implode("</li>\n<li>", $files) . '</li></ul>';
            }

            $version = $suffix[0] . '.' . $suffix[1];
            $compilations []= "<tr><td>$version</td><td>$total</td><td>$total_error</td><td>$files</td><td>$errors</td></tr>";
        }

        $compilations = implode(PHP_EOL, $compilations);
        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'COMPILATIONS', $compilations);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function generateCompatibility82(Section $section): void {
        $this->generateCompatibility($section, '82');
    }

    protected function generateCompatibility81(Section $section): void {
        $this->generateCompatibility($section, '81');
    }

    protected function generateCompatibility80(Section $section): void {
        $this->generateCompatibility($section, '80');
    }

    protected function generateCompatibility74(Section $section): void {
        $this->generateCompatibility($section, '74');
    }

    protected function generateCompatibility73(Section $section): void {
        $this->generateCompatibility($section, '73');
    }

    protected function generateCompatibility72(Section $section): void {
        $this->generateCompatibility($section, '72');
    }

    protected function generateCompatibility71(Section $section): void {
        $this->generateCompatibility($section, '71');
    }

    protected function generateCompatibility70(Section $section): void {
        $this->generateCompatibility($section, '70');
    }

    protected function generateCompatibility56(Section $section): void {
        $this->generateCompatibility($section, '56');
    }

    protected function generateCompatibility55(Section $section): void {
        $this->generateCompatibility($section, '55');
    }

    protected function generateCompatibility54(Section $section): void {
        $this->generateCompatibility($section, '54');
    }

    protected function generateCompatibility53(Section $section): void {
        $this->generateCompatibility($section, '53');
    }

    protected function generateCompatibility(Section $section, string $version): void {
        $compatibility = array();
        $skipped       = array();

        $list = $this->rulesets->getRulesetsAnalyzers(array('CompatibilityPHP' . $version));

        $res = $this->dump->fetchAnalysersCounts($list);
        $counts = $res->toHash('analyzer', 'count');

        foreach($list as $analyzer) {
            $ini = $this->docs->getDocs($analyzer);
            if (isset($counts[$analyzer])) {
                $resultState = (int) $counts[$analyzer];
            } else {
                $resultState = -2; // -2 === not run
            }
            $result = $this->Compatibility($resultState, $analyzer);
            $link = '<a href="analyses_doc.html#' . $this->toId($analyzer) . '" alt="Documentation for ' . $ini['name'] . '"><i class="fa fa-book"></i></a>';
            if ($resultState === Analyzer::VERSION_INCOMPATIBLE) {
                $skipped []= "<tr><td>$link {$ini['name']}</td><td>$result</td></tr>\n";
            } else {
                $compatibility []= "<tr><td>$link {$ini['name']}</td><td>$result</td></tr>\n";
            }
        }
        $compatibility = implode(PHP_EOL, $compatibility) . PHP_EOL . implode(PHP_EOL, $skipped);

        $description = <<<'HTML'
<i class="fa fa-check-square-o"></i> : Nothing found for this analysis, proceed with caution; <i class="fa fa-warning red"></i> : some issues found, check this; <i class="fa fa-ban"></i> : Can't test this, PHP version incompatible; <i class="fa fa-cogs"></i> : Can't test this, PHP configuration incompatible; 
HTML;

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'COMPATIBILITY', $compatibility);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', $description);
        $this->putBasedPage($section->file, $html);
    }

    private function generateDynamicCode(Section $section): void {
        $dynamicCode = '';

        $results = $this->dump->fetchAnalysers(array('Structures/DynamicCode'));

        foreach($results->toArray() as $row) {
            $dynamicCode .= "<tr><td>{$row['htmlcode']}</td><td>{$row['file']}</td><td>{$row['line']}</td></tr>\n";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'DYNAMIC_CODE', $dynamicCode);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateGlobals(Section $section): void {
        $res = $this->dump->fetchTable('globalVariables');

        if ($res->isEmpty()) {
            $this->emptyResult($section);
            return;
        }

        $tree = array();
        foreach($res->toArray() as $row) {
            $variable = trim($row['variable'], '{}&@');
            $name = preg_replace('/^\$GLOBALS\[[ \'"]*(.*?)[ \'"]*\]$/', '$\1', $variable);
            if (substr($variable, 0, 8) === '$GLOBALS') {
                $origin = '$GLOBALS';
            } else {
                $origin = 'global';
            }

            if (isset($tree[$name])) {
                ++$tree[$name]['count'];
                $tree[$name]['file'][]       = $row['file'] . ':' . $row['line'];
                $tree[$name]['type'][]       = $row['type'];
                $tree[$name]['status'][]     = ($row['isRead'] ? 'R' : '&nbsp;' ) . ' - ' . ($row['isModified'] ? 'W' : '&nbsp;' );
            } else {
                $tree[$name]['count']      = 1;
                $tree[$name]['file']       = array($row['file'] . ':' . $row['line']        );
                $tree[$name]['type']       = array($row['type']);
                $tree[$name]['status']     = array(($row['isRead'] ? 'R' : '&nbsp;' ) . ' - ' . ($row['isModified'] ? 'W' : '&nbsp;' ));
            }
        }

        uasort($tree, function (array $a, array $b): int { return $a['count'] <=> $b['count'];});

        $theGlobals = array();
        foreach($tree as $variable => $details) {
            $count      = $details['count'];
            $types      = implode('<br />', $details['type']);
            $status     = implode('<br />', $details['status']);
            $files      = implode('<br />', $details['file']);

            $theGlobals []= "<tr><td><span style=\"color: #0000BB\">$variable</span></td><td>$count</td><td>$types</td><td>$status</td><td>$files</td></tr>\n";
        }
        $theGlobals = implode('', $theGlobals);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'GLOBALS', $theGlobals);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateInventoriesConstants(Section $section): void {
        $this->generateInventories($section, array('Constants/Constantnames'), 'List of all defined constants in the code.');
    }

    private function generateInventoriesClasses(Section $section): void {
        $this->generateInventories($section, array('Classes/Classnames'), 'List of all defined classes in the code.');
    }

    private function generateInventoriesInterfaces(Section $section): void {
        $this->generateInventories($section, array('Interfaces/Interfacenames'), 'List of all defined interfaces in the code.');
    }

    private function generateInventoriesTraits(Section $section): void {
        $this->generateInventories($section, array('Traits/Traitnames'), 'List of all defined traits in the code.');
    }

    private function generateInventoriesFunctions(Section $section): void {
        $this->generateInventories($section, array('Functions/Functionnames'), 'List of all defined functions in the code.');
    }

    private function generateInventoriesNamespaces(Section $section): void {
        $this->generateInventories($section, array('Namespaces/Namespacesnames'), 'List of all defined namespaces in the code.');
    }

    private function generateInventoriesUrl(Section $section): void {
        $this->generateInventories($section, array('Type/Url'), 'List of all URL mentioned in the code.');
    }

    private function generateInventoriesRegex(Section $section): void {
        $this->generateInventories($section, array('Type/Regex'), 'List of all Regex mentioned in the code.');
    }

    private function generateInventoriesSql(Section $section): void {
        $this->generateInventories($section, array('Type/Sql'), 'List of all SQL mentioned in the code.');
    }

    private function generateInventoriesGPCIndex(Section $section): void {
        $this->generateInventories($section, array('Type/GPCIndex'), 'List of all Email mentioned in the code.');
    }

    private function generateInventoriesEmail(Section $section): void {
        $this->generateInventories($section, array('Type/Email'), 'List of all incoming variables mentioned in the code.');
    }

    private function generateInventoriesMd5string(Section $section): void {
        $this->generateInventories($section, array('Type/Md5string'), 'List of all MD5-like strings mentioned in the code.');
    }

    private function generateInventoriesMime(Section $section): void {
        $this->generateInventories($section, array('Type/MimeType'), 'List of all Mime strings mentioned in the code.');
    }

    private function generateInventoriesPack(Section $section): void {
        $this->generateInventories($section, array('Type/Pack'), 'List of all packing format strings mentioned in the code.');
    }

    private function generateInventoriesPrintf(Section $section): void {
        $this->generateInventories($section, array('Type/Printf'), 'List of all printf(), sprintf(), etc. formats strings mentioned in the code.');
    }

    private function generateInventoriesPath(Section $section): void {
        $this->generateInventories($section, array('Type/Path'), 'List of all paths strings mentioned in the code.');
    }

    private function generateInventories(Section $section, array $analyzer, string $description): void {
       $results = $this->dump->fetchAnalysers($analyzer);

       $groups = array();
       foreach($results->toArray() as $row) {
           $groups[$row['htmlcode']][] = $row['file'];
       }
       uasort($groups, function (array $a, array $b): int { return count($a) <=> count($b);});

       $theTable = array();
       foreach($groups as $code => $list) {
           $c = count($list);
           $htmlList = '<ul><li>' . implode('</li><li>', $list) . '</li></ul>';
           $theTable []= "<tr><td>{$code}</td><td>$c</td><td>{$htmlList}</td></tr>";
       }

       $html = $this->getBasedPage($section->source);
       $html = $this->injectBloc($html, 'TITLE', $section->title);
       $html = $this->injectBloc($html, 'DESCRIPTION', $description);
       $html = $this->injectBloc($html, 'TABLE', implode(PHP_EOL, $theTable));
       $this->putBasedPage($section->file, $html);
    }

    private function generateInterfaceTree(Section $section): void {
        $res = $this->dump->getCitTree('interface');
        foreach($res->toArray() as $row) {
            if (empty($row['parent'])) {
                continue;
            }

            $parent = $row['parent'];
            if (!isset($list[$parent])) {
                $list[$parent] = array();
            }

            $list[$parent][] = $row['child'];
        }

        if (empty($list)) {
            $theTable = 'No interface were found in this repository.';
        } else {
            array_sub_sort($list);

            $secondaries = array_merge(...array_values($list));
            $top = array_diff(array_keys($list), $secondaries);

            $theTableArray = array();
            foreach($top as $t) {
                $theTableArray[] = '<ul class="tree">' . $this->extends2ul($t, $list) . '</ul>';
            }
            $theTable = implode(PHP_EOL, $theTableArray);
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Here are the interface trees : the interfaces that are extended by another interface. Interface without extension are not represented here');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);

    }

    private function generateConstantTree(Section $section): void {
        $list = array();
        $res = $this->dump->fetchTable('constantOrder');
        foreach($res->toArray() as $row) {
            if (empty($row['built'])) {
                continue;
            }

            $built = $row['built'];
            if (!isset($list[$built])) {
                $list[$built] = array();
            }

            $list[$built][] = $row['building'];
        }

        if (empty($list)) {
            $theTable = 'No structured constant were found in this repository.';
        } else {
            array_sub_sort($list);

            $secondaries = array_merge(...array_values($list));
            $top = array_diff(array_keys($list), $secondaries);

            $theTableArray = array();
            foreach($top as $t) {
                $theTableArray[] = '<ul class="tree">' . $this->extends2ul($t, $list) . '</ul>';
            }
            $theTable = implode(PHP_EOL, $theTableArray);
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Here are the constant trees : a constant (global or class) is build based on another constant. Constants built only with literals are not represented here.');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);

    }

    private function generateTraitMatrix(Section $section): void {
        // list of all traits, for building the table
        $traits = $this->dump->getCit('trait')->getColumn('name');

        // INIT
        $table = array_fill_keys($traits, array_fill_keys($traits, array()));

        // Get conflicts
        $res = $this->dump->getTraitConflicts();
        foreach($res->toArray() as $row) {
            $table[$row['t1']][$row['t2']][] = $row['method'];
        }

        // Get trait usage
        $res = $this->dump->getTraitUsage();
        $usage = $res->toHash('t1', 't2');

        $rows = array();
        foreach($table as $name => $row) {
            $closure = function (&$r, $t2) use ($name, $usage): void {
                $content = empty($r) ? '&nbsp;' : implode('(), ', $r) . '()';
                $background = isset($usage[$name][$t2]) ? ' bgcolor="darkgray"' : '';

                $r = "<td$background>$content</td>";
            };
            array_walk($row, $closure);
            $row = implode('', $row);

            $rows[] = "<tr><td>$name</td>$row</tr>\n";
        }

        $cells = implode('</td><td>', $traits);
        $rows = implode('', $rows);
        $theTable = <<<HTML
<table class="table table-striped">
    <tr>
        <td>&nbsp;</td>
        <td>$cells</td>
    </tr>
    $rows
</table>
HTML;

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Here are the trait matrix. Conflicting methods between any two traits are listed in the cells : when they are used in the same class, those traits will require conflict resolutions. And dark gray cells are traits that are actually included one into the other.');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);
    }

    private function generateTraitTree(Section $section): void {
        $list = array();

        $res = $this->dump->getCitTree('trait');
        foreach($res->toArray() as $row) {
            if (empty($row['child'])) {
                continue;
            }

            $parent = $row['child'];
            if (!isset($list[$parent])) {
                $list[$parent] = array();
            }

            $list[$parent][] = $row['parent'];
        }

        if (empty($list)) {
            $theTable = 'No trait were found in this repository.';
        } else {
            array_sub_sort($list);

            $theTable = array();
            foreach(array_keys($list) as $t) {
                $theTable[] = '<ul class="tree">' . $this->extends2ul($t, $list) . '</ul>';
            }
            $theTable = implode(PHP_EOL, $theTable);
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Here are the extension trees of the traits : a trait is extended when it uses another trait. Traits without any extension are not represented. The same trait may be mentionned several times, as trait may use an arbitrary number of traits.');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);
    }

    private function generateClassTree(Section $section): void {
        $list = array();

        $res = $this->dump->getCitTree('class');
        foreach($res->toArray() as $row) {
            if (empty($row['parent'])) {
                continue;
            }

            $parent = $row['parent'];
            if (!isset($list[$parent])) {
                $list[$parent] = array();
            }

            $list[$parent][] = $row['child'];
        }

        if (empty($list)) {
            $theTable = 'No class were found in this repository.';
        } else {
            array_sub_sort($list);

            $secondaries = array_merge(...array_values($list));
            $top = array_diff(array_keys($list), $secondaries);

            $theTable = array();
            foreach($top as $t) {
                $theTable[] = '<ul class="tree">' . $this->extends2ul($t, $list) . '</ul>';
            }
            $theTable = implode(PHP_EOL, $theTable);
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', 'Classes Tree');
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Here are the classes tree, built with class extensions. Classes without any extension are not represented.');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);
    }

    private function extends2ul(string $root, array $paths, int $level = 0): string {
        static $done = array();

        if ($level === 0) {
            $done = array();
        }

        $return = array();
        foreach($paths[$root] as $sub) {
            if (isset($paths[$sub])){
                if (!isset($done[$sub]) && $level < 10) {
                    $done[$sub] = 1;
                    $secondary = $this->extends2ul($sub, $paths, $level + 1);
                    $return[] = $secondary;
                } else {
                    $return[] = '<li>' . $sub . '...(Recursive)</li>';
                }
            } else {
                $return[] = "<li class=\"treeLeaf\">$sub</li>";
                $done[$sub] = 1;
            }
        }
        $return = "<li>$root<ul>" . implode('', $return) . "</ul></li>\n";

        return $return;
    }

    private function generateAttributeInventory(Section $section): void {
        $res = $this->dump->fetchTable('cit');
        $cit = array();
        foreach($res->toArray() as $row) {
            $cit[$row['type']][$row['id']] = $row['name'];
        }

        $res = $this->dump->fetchTable('functions');
        $functions = array();
        foreach($res->toArray() as $row) {
            $functions[$row['id']] = $row['function'];
        }

        $res = $this->dump->fetchTable('methods');
        $methods = array();
        foreach($res->toArray() as $row) {
            $methods[$row['id']] = $row['method'];
        }

        $res = $this->dump->fetchTable('classconstants');
        $classconstants = array();
        foreach($res->toArray() as $row) {
            $classconstants[$row['id']] = $row['constant'];
        }

        $res = $this->dump->fetchTable('arguments');
        $arguments = array();
        foreach($res->toArray() as $row) {
            $arguments[$row['id']] = $row['name'];
        }

        $theTable = array();
        $res = $this->dump->fetchTable('attributes');
        foreach($res->toArray() as $row) {
            // todo Add al lthe other types
            // todo add color syntax
            switch ($row['type']) {
                case 'class' :
                    $location = $row['type'] . ' ' . $cit[$row['type']][$row['type_id']] . '';
                    break;

                case 'function' :
                   $location = 'function ' . $functions[$row['type_id']] . ' ()';
                    break;

                case 'method' :
                   $location = 'function ' . $methods[$row['type_id']] . ' ()';
                    break;

                case 'argument' :
                    $location = $arguments[$row['type_id']];
                    break;


                case 'classconstant' :
                    $location = 'const ' . $classconstants[$row['type_id']];
                    break;

                default:
                    assert(false, "No such type as $row[type]");
            }
            $theTable[] = '<tr><td>' . $row['attribute'] . '</td><td>' . $row['type'] . '</td><td>' . $location . '</td></tr>';
        }
        sort($theTable);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'List of attributes in use in the code');
        $html = $this->injectBloc($html, 'TABLE', implode(PHP_EOL, $theTable));
        $this->putBasedPage($section->file, $html);
    }

    private function generateExceptionTree(Section $section): void {
        $exceptions = array (
  'Throwable' =>
  array (
    'Error' =>
    array (
      'ParseError' =>
      array (
      ),
      'TypeError' =>
      array (
        'ArgumentCountError' =>
        array (
        ),
      ),
      'ArithmeticError' =>
      array (
        'DivisionByZeroError' =>
        array (
        ),
      ),
      'AssertionError' =>
      array (
      ),
    ),
    'Exception' =>
    array (
      'ErrorException' =>
      array (
      ),
      'ClosedGeneratorException' =>
      array (
      ),
      'DOMException' =>
      array (
      ),
      'LogicException' =>
      array (
        'BadFunctionCallException' =>
        array (
          'BadMethodCallException' =>
          array (
          ),
        ),
        'DomainException' =>
        array (
        ),
        'InvalidArgumentException' =>
        array (
        ),
        'LengthException' =>
        array (
        ),
        'OutOfRangeException' =>
        array (
        ),
      ),
      'RuntimeException' =>
      array (
        'OutOfBoundsException' =>
        array (
        ),
        'OverflowException' =>
        array (
        ),
        'RangeException' =>
        array (
        ),
        'UnderflowException' =>
        array (
        ),
        'UnexpectedValueException' =>
        array (
        ),
        'PDOException' =>
        array (
        ),
      ),
      'PharException' =>
      array (
      ),
      'ReflectionException' =>
      array (
      ),
    ),
  ),
);
        $list = array();

        $theTable = '';
        $res = $this->dump->fetchAnalysers(array('Exceptions/DefinedExceptions'));
        foreach($res->toArray() as $row) {
            if (!preg_match('/ extends (\S+)/', $row['fullcode'], $r)) {
                continue;
            }

            $parent = strtolower($r[1]);
            if ($parent[0] !== '\\') {
                $parent = "\\$parent";
            }

            if (!isset($list[$parent])) {
                $list[$parent] = array();
            }

            $class = str_replace(' { /**/ }', '', $row['fullcode']);
            $class = str_replace('final ', '', $class);
            $class = PHPSyntax($class);
            $list[$parent][] = $class;
        }

        array_sub_sort($list);
        $theTable = $this->tree2ul($exceptions, $list);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', '');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);
    }

    private function path2tree(array $paths): array {
        $return = array();

        $recursive = array();
        foreach($paths as $path) {
            $folders = explode('\\', $path);

            $first = empty($folders[0]) ? '\\' : $folders[0];

            if (!isset($return[$first])) {
                $return[$first] = array();
            }

            if (count($folders) > 2) {
                $recursive[$first] = 1;
            }
            $return[$first][] = implode('\\', array_slice($folders, 1));
        }

        foreach(array_keys($recursive) as $recurrent) {
            $return[$recurrent] = $this->path2tree($return[$recurrent]);
        }

        return $return;
    }

    private function pathtree2ul(array $path): string {
        if (empty($path)) {
            return '';
        }
        $return = array('<ul>');

        foreach($path as $k => $v) {
            $return []= '<li>';

            if (is_string($v)) {
                if (empty($v)) {
                    $return []= '<div style="font-weight: bold">\\</div>';
                } else {
                    $return []= '<div style="font-weight: bold">' . $v . '</div>';
                }
            } elseif (count($v) === 1) {
                if (empty($v[0])) {
                    if (empty($k)) {
                        $return []= '<div style="font-weight: bold">\\</div>';
                    } else {
                        $return []= '<div style="font-weight: bold">' . $k . '</div>';
                    }
                } else {
                    $return []= '<div style="font-weight: bold">' . $k . '</div>' . $this->pathtree2ul($v);
                }
            } else {
                $return []= '<div style="font-weight: bold">' . $k . '</div>' . $this->pathtree2ul($v);
            }

            $return []= '</li>';
        }
        $return []= '</ul>';

        return implode('', $return);
    }

    private function generateNamespaceTree(Section $section): void {
        $theTable = '';
        $res = $this->dump->fetchTable('namespaces');
        $res->order(function (array $a, array $b): int { return $a['namespace'] <=> $b['namespace'];});
                $res->map(function (array $x): array { $x['namespace'] = trim($x['namespace'], '\\'); return $x;});
        $paths = $res->getColumn('namespace');

        $paths = $this->path2tree($paths);
        $theTable = $this->pathtree2ul($paths);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Here are the various namespaces in use in the code.');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);
    }

    private function tree2ul(array $tree,array $display): string {
        if (empty($tree)) {
            return '';
        }
        $return = '<ul>';

        foreach($tree as $k => $v) {
            $return .= '<li>';

            $parent = '\\' . strtolower((string) $k);
            if (isset($display[$parent])) {
                $return .= '<div style="font-weight: bold">' . $k . '</div><ul><li>' . implode('</li><li>', $display[$parent]) . '</li></ul>';
            } else {
                $return .= '<div style="font-weight: bold; color: darkgray">' . $k . '</div>';
            }

            if (is_array($v)) {
                $return .= $this->tree2ul($v, $display);
            }

            $return .= '</li>';
        }

        $return .= '</ul>';

        return $return;
    }

    private function generateVisibilitySuggestions(Section $section): void {
        $constants  = $this->generateVisibilityConstantSuggestions();
        $properties = $this->generateVisibilityPropertySuggestions();
        $methods    = $this->generateVisibilityMethodsSuggestions();

        $classes = array_unique(array_merge(array_keys($constants),
                                            array_keys($properties),
                                            array_keys($methods)));

        $visibilityTable = array(<<<'HTML'
<table class="table table-striped">
    <tr>
        <td>&nbsp;</td>
        <td>Name</td>
        <td>Value</td>
        <td>None (public)</td>
        <td>Public</td>
        <td>Protected</td>
        <td>Private</td>
        <td>Constant</td>
    </tr>
HTML
);

        foreach($classes as $id) {
            list(, $class) = explode(':', $id);
            $visibilityTable []= '<tr><td colspan="9">class ' . PHPSyntax($class) . '</td></tr>' . PHP_EOL .
                                (isset($constants[$id]) ? implode('', $constants[$id]) : '') .
                                (isset($properties[$id]) ? implode('', $properties[$id]) : '') .
                                (isset($methods[$id]) ? implode('', $methods[$id]) : '');
        }

        $visibilityTable []= '</table>';
        $visibilityTable = implode(PHP_EOL, $visibilityTable);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Below, is a summary of all classes and their component\'s visiblity. Whenever a visibility is set and used at the right level, a green star is presented. Whenever it is set to a level, but could be updated to another, red and orange stars are mentioned. ');
        $html = $this->injectBloc($html, 'CONTENT', $visibilityTable);
        $this->putBasedPage($section->file, $html);
    }

    private function generateClassOptionSuggestions(Section $section): void {
        $finals  = $this->generateClassFinalSuggestions();
        $abstracts = $this->generateClassAbstractuggestions();

        $classes = array_unique(array_merge($finals, $abstracts));

        $visibilityTable = array();
        foreach($classes as $path => $fullcode) {
            $class = str_replace('{ /**/ } ', '', $fullcode);

            if (isset($finals[$path])) {
                $final =  '<i class="fa fa-star" style="color:red"></i>';
            } elseif (stripos($fullcode, 'final') !== false) {
                $final =  '&nbsp';
            } else {
                $final =  '<i class="fa fa-star" style="color:green"></i>';
            }

            if (isset($abstracts[$path])) {
                $abstract =  '<i class="fa fa-star" style="color:red"></i>';
            } elseif (stripos($fullcode, 'abstract') !== false) {
                $abstract =  '&nbsp';
            } else {
                $abstract =  '<i class="fa fa-star" style="color:green"></i>';
            }

            $visibilityTable[] = <<<HTML
<tr>
    <td colspan=\"9\">$final</td>
    <td colspan=\"9\">$abstract</td>
    <td colspan=\"9\">$class</td>
    <td colspan=\"9\">$path</td>
</tr>

HTML;
        }
        $visibilityTable = implode(PHP_EOL, $visibilityTable);

        $visibilityHtml = <<<HTML
<table class="table table-striped">
    <tr>
        <td>Final</td>
        <td>Abstract</td>
        <td>Name</td>
        <td>Path</td>
    </tr>
    $visibilityTable
    </table>
HTML;

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', <<<'HTML'
Below, is a list of classes that may be updated with final or abstract. <br />

The red stars <i class="fa fa-star" style="color:red"></i> mention possible upgrade by using final or abstract keywords; 
The green stars <i class="fa fa-star" style="color:green"></i> mention a valid absence of the option (an extended class, that can't be final, ...); 
The absence of star report currently configured classes.  

HTML
);
        $html = $this->injectBloc($html, 'CONTENT', $visibilityHtml);
        $this->putBasedPage($section->file, $html);
    }

    private function generateClassFinalSuggestions(): array {
        $res = $this->dump->fetchAnalysers(array('Classes/CouldBeFinal'));

        $couldBeFinal = array();
        foreach($res->toArray() as $row) {
            if (!preg_match('/(class|interface|trait) (\S+) /i', $row['fullcode'], $classname)) {
                continue;
            }
            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[2]);

            $couldBeFinal[$fullnspath] = $row['fullcode'];
        }

        return $couldBeFinal;
    }

    private function generateClassAbstractuggestions(): array {
        $res = $this->dump->fetchAnalysers(array('Classes/CouldBeAbstractClass'));

        $couldBeAbstract = array();
        foreach($res->toArray() as $row) {
            if (!preg_match('/(class|interface|trait) (\S+) /i', $row['fullcode'], $classname)) {
                continue;
            }
            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[2]);

            $couldBeAbstract[$fullnspath] = $row['fullcode'];
        }

        return $couldBeAbstract;
    }

    private function generateVisibilityMethodsSuggestions(): array {
        $res = $this->dump->fetchAnalysers(array('Classes/CouldBePrivateMethod'));

        $couldBePrivate = array();
        foreach($res->toArray() as $row) {
            if (!preg_match('/(class|interface|trait) (\S+) /i', $row['class'], $classname)) {
                // todo : should log this
                continue;
            }

            if (!preg_match('/(function) (\S+?)\(/i', $row['fullcode'], $methodname)) {
                // todo : should log this
                continue;
            }

            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[2]) . '::' . mb_strtolower($methodname[2]);

            if (isset($couldBePrivate[$fullnspath])) {
                $couldBePrivate[$fullnspath][] = $row['fullcode'];
            } else {
                $couldBePrivate[$fullnspath] = array($row['fullcode']);
            }
        }

        $res = $this->dump->fetchAnalysers(array('Classes/CouldBeProtectedMethod'));
        $couldBeProtected = array();
        foreach($res->toArray() as $row) {
            if (!preg_match('/(class|interface|trait) (\S+) /i', $row['class'], $classname)) {
                // todo : should log this
                continue;
            }

            if (!preg_match('/(function) (\S+?)\(/i', $row['fullcode'], $methodname)) {
                // todo : should log this
                continue;
            }

            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[2]) . '::' . mb_strtolower($methodname[2]);

            if (isset($couldBeProtected[$fullnspath])) {
                $couldBeProtected[$fullnspath][] = $row['fullcode'];
            } else {
                $couldBeProtected[$fullnspath] = array($row['fullcode']);
            }
        }

        $res = $this->dump->fetchTableMethods();
        $res->filter(function (array $x): bool { return $x['type'] === 'class'; });

        $ranking = array(''          => 0,
                         'none'      => 0,
                         'public'    => 1,
                         'protected' => 2,
                         'private'   => 3);

        $return = array();
        $theClass = '';
        $aClass = array();

        foreach($res->toArray() as $row) {
            if ($theClass != $row['fullnspath'] . ':' . $row['class']) {
                $return[$theClass] = $aClass;
                $theClass = $row['fullnspath'] . ':' . $row['class'];
                $aClass = array();
            }

            $visibilities = array('&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;');
            $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:green"></i>';

            if (isset($couldBePrivate[$row['fullnspath']])) {
                $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:red"></i>';
                $visibilities[$ranking['private']] = '<i class="fa fa-star" style="color:green"></i>';
            }

            if (isset($couldBeProtected[$row['fullnspath']])) {
                $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:red"></i>';
                $visibilities[$ranking['protected']] = '<i class="fa fa-star" style="color:#FFA700"></i>';
            }

            $aClass[] = '<tr><td>&nbsp;</td><td>' . PHPSyntax($row['method']) . '</td><td class="exakat_short_text">' .
                                    implode('</td><td>', $visibilities)
                                 . '</td></tr>' . PHP_EOL;
        }

        $return[$theClass] = $aClass;
        unset($return['']);

        return $return;
    }

    private function generateVisibilityConstantSuggestions(): array {
        $res = $this->dump->fetchAnalysers(array('Classes/CouldBePrivateConstante'));

        $couldBePrivate = array();
        foreach($res->toArray() as $row) {
            if (!preg_match('/class (\S+) /i', $row['class'], $classname)) {
                continue; // it is an interface or a trait
            }

            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[1]);

            if (!preg_match('/^(.+) = /i', $row['fullcode'], $code)) {
                continue;
            }

            if (isset($couldBePrivate[$fullnspath])) {
                $couldBePrivate[$fullnspath][] = $code[1];
            } else {
                $couldBePrivate[$fullnspath] = array($code[1]);
            }
        }

        $res = $this->dump->fetchAnalysers(array('Classes/CouldBeProtectedConstant'));
        $couldBeProtected = array();
        foreach($res->toArray() as $row) {
            if (!preg_match('/class (\S+) /i', $row['class'], $classname)) {
                continue; // it is an interface or a trait
            }
            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[1]);
            if (!preg_match('/^(.+) = /i', $row['fullcode'], $code)) {
                // todo : should log this
                continue;
            }

            if (isset($couldBeProtected[$fullnspath])) {
                $couldBeProtected[$fullnspath][] = $code[1];
            } else {
                $couldBeProtected[$fullnspath] = array($code[1]);
            }
        }

        $res = $this->dump->fetchTableClassConstants();
        $res->filter(function (array $x): bool { return $x['type'] === 'class'; });

        $theClass = '';
        $ranking = array(''          => 1,
                         'public'    => 2,
                         'protected' => 3,
                         'private'   => 4,
                         'constant'  => 5);
        $return = array();

        $aClass = array();
        foreach($res->toArray() as $row) {
            if ($theClass != $row['fullnspath'] . ':' . $row['class']) {
                $return[$theClass] = $aClass;
                $theClass = $row['fullnspath'] . ':' . $row['class'];
                $aClass = array();
            }

            $visibilities = array(PHPSyntax((string) $row['value']), '&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;');
            $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:green"></i>';

            if (isset($couldBePrivate[$row['fullnspath']]) &&
                in_array($row['constant'], $couldBePrivate[$row['fullnspath']], \STRICT_COMPARISON)) {
                    $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:red"></i>';
                    $visibilities[$ranking['private']] = '<i class="fa fa-star" style="color:green"></i>';
            }

            if (isset($couldBeProtected[$row['fullnspath']]) &&
                in_array($row['constant'], $couldBeProtected[$row['fullnspath']], \STRICT_COMPARISON)) {
                    $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:red"></i>';
                    $visibilities[$ranking['protected']] = '<i class="fa fa-star" style="color:#FFA700"></i>';
            }

            $aClass[] = '<tr><td>&nbsp;</td><td>' . PHPSyntax($row['constant']) . '</td><td class="exakat_short_text">' .
                                    implode('</td><td>', $visibilities)
                                 . '</td></tr>' . PHP_EOL;
        }

        $return[$theClass] = $aClass;
        unset($return['']);

        return $return;
    }

    private function generateVisibilityPropertySuggestions(): array {

        $res = $this->dump->fetchAnalysers(array('Classes/CouldBePrivate'));
        $couldBePrivate = array();
        foreach($res->toArray() as $row) {
            preg_match('/(class|trait) (\S+) /i', $row['class'], $classname);
            assert(isset($classname[1]), 'Missing class in ' . $row['class']);
            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[2]);

            preg_match('/(\$\S+)/i', $row['fullcode'], $code);
            assert(isset($code[1]), 'Missing class in ' . $row['fullcode']);

            if (isset($couldBePrivate[$fullnspath])) {
                $couldBePrivate[$fullnspath][] = $code[1];
            } else {
                $couldBePrivate[$fullnspath] = array($code[1]);
            }
        }

        $res = $this->dump->fetchAnalysers(array('Classes/CouldBeProtectedProperty'));
        $couldBeProtected = array();
        foreach($res->toArray() as $row) {
            preg_match('/(class|trait) (\S+) /i', $row['class'], $classname);
            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[1]);

            preg_match('/(\$\S+)/', $row['fullcode'], $code);

            if (isset($couldBeProtected[$fullnspath])) {
                $couldBeProtected[$fullnspath][] = $code[1];
            } else {
                $couldBeProtected[$fullnspath] = array($code[1]);
            }
        }

        $res = $this->dump->fetchAnalysers(array('Classes/CouldBeClassConstant'));
        $couldBeConstant = array();
        foreach($res->toArray() as $row) {
            preg_match('/(class|trait) (\S+) /i', $row['class'], $classname);
            $fullnspath = $row['namespace'] . '\\' . strtolower($classname[1]);

            preg_match('/(\$\S+)/', $row['fullcode'], $code);

            if (isset($couldBeConstant[$fullnspath])) {
                $couldBeConstant[$fullnspath][] = $code[1];
            } else {
                $couldBeConstant[$fullnspath] = array($code[1]);
            }
        }

        $res = $this->dump->fetchTableProperty();
        $res->filter(function (array $x): bool { return $x['type'] === 'class'; });

        $theClass = '';
        $ranking = array(''          => 1,
                         'none'      => 1,
                         'public'    => 2,
                         'protected' => 3,
                         'private'   => 4,
                         'constant'  => 5);
        $return = array();

        $aClass = array();
        foreach($res->toArray() as $row) {
            if ($theClass != $row['fullnspath'] . ':' . $row['class']) {
                $return[$theClass] = $aClass;
                $theClass = $row['fullnspath'] . ':' . $row['class'];
                $aClass = array();
            }

            list($row['property']) = explode(' = ', $row['property'], 1);

            $visibilities = array(PHPSyntax($row['value']), '&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;', '&nbsp;');
            $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:green"></i>';

            if (isset($couldBePrivate[$row['fullnspath']]) &&
                in_array($row['property'], $couldBePrivate[$row['fullnspath']], \STRICT_COMPARISON)) {
                    $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:red"></i>';
                    $visibilities[$ranking['private']] = '<i class="fa fa-star" style="color:green"></i>';
            }

            if (isset($couldBeProtected[$row['fullnspath']]) &&
                in_array($row['property'], $couldBeProtected[$row['fullnspath']], \STRICT_COMPARISON)) {
                    $visibilities[$ranking[$row['visibility']]] = '<i class="fa fa-star" style="color:red"></i>';
                    $visibilities[$ranking['protected']] = '<i class="fa fa-star" style="color:#FFA700"></i>';
            }

            if (isset($couldBeConstant[$row['fullnspath']]) &&
                in_array($row['property'], $couldBeConstant[$row['fullnspath']], \STRICT_COMPARISON)) {
                    $visibilities[$ranking['constant']] = '<i class="fa fa-star" style="color:black"></i>';
            }

            $aClass[] = '<tr><td>&nbsp;</td><td>' . PHPSyntax($row['property']) . '</td><td class="exakat_short_text">' .
                            implode('</td><td>', $visibilities)
                            . '</td></tr>' . PHP_EOL;
        }
        $return[$theClass] = $aClass;
        unset($return['']);

        return $return;
    }

    private function generateAlteredDirectives(Section $section): void {
        $alteredDirectives = array();
        $res = $this->dump->fetchAnalysers(array('Php/DirectivesUsage'));
        foreach($res->toArray() as $row) {
            $alteredDirectives []= '<tr><td>' . PHPSyntax($row['fullcode']) . "</td><td>$row[file]</td><td>$row[line]</td></tr>";
        }
        $alteredDirectives = implode(PHP_EOL, $alteredDirectives);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'ALTERED_DIRECTIVES', $alteredDirectives);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateChangedClasses(Section $section): void {
        $changedClasses = '';
        $res = $this->dump->fetchTable('classChanges');

        if ($res->isEmpty() === true) {
            $changedClasses = 'No changes detected';
        } else {
            foreach($res->toArray() as $row) {
                if ($row['changeType'] === 'Member Visibility') {
                    $row['parentValue'] .= ' $' . $row['name'];
                    $row['childValue']   = ' $' . $row['name'];
                } elseif ($row['changeType'] === 'Member Default') {
                    $row['parentValue'] = '$' . $row['name'] . ' = ' . $row['parentValue'];
                    $row['childValue']  = '$' . $row['name'] . ' = ' . $row['childValue'];
                }

                $changedClasses .= '<tr><td>' . PHPSyntax($row['parentClass']) . '</td>' . PHP_EOL .
                                       '<td>' . PHPSyntax($row['parentValue']) . '</td>' . PHP_EOL .
                                       '</tr><tr>' .
                                       '<td>' . PHPSyntax($row['childClass']) . '</td>' . PHP_EOL .
                                       '<td>' . PHPSyntax($row['childValue']) . '</td>' . PHP_EOL .
                                       '</tr>' . PHP_EOL .
                                       '<tr><td colspan="2"><hr /></td></tr>';
            }
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'CHANGED_CLASSES', $changedClasses);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->source, $html);
    }

    private function emptyResult(Section $section): void {
        $finalHTML = $this->getBasedPage('empty');

        $finalHTML = $this->injectBloc($finalHTML, 'DESCRIPTION',  'No result were found for this analysis.');
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'CONTENT', '');
        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateParameterNames(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        $variables = $this->dump->fetchTable('variables')->getColumn('variable');
        $variables = array_flip($variables);

        $res = $this->dump->fetchTable('arguments');
        $parameters = $res->toGroupedCount('name');
        uasort($parameters, function ($a, $b): int { return $b <=> $a;});

        $typehints = array();
        foreach($res->toArray() as $row) {
            if (empty($row['typehint'])) { continue; }
            if (!isset($typehints[$row['name']])) {
                $typehints[$row['name']] = array($row['typehint']);

                continue;
            }

            $typehints[$row['name']][] = $row['typehint'];
        }
        $typehints = array_map('array_unique', $typehints);

        $defaults  = array();
        foreach($res->toArray() as $row) {
            if (empty($row['init'])) { continue; }
            if (!isset($defaults[$row['name']])) {
                $defaults[$row['name']] = array($row['init']);

                continue;
            }

            $defaults[$row['name']][] = $row['init'];
        }
        $defaults = array_map('array_unique', $defaults);

        $html = array();
        foreach ($parameters as $variable => $count) {
            $html []= '<tr>
                      <td>' . $variable . '</td>
                      <td>' . $count . '</td>
                      <td>' . (isset($variables[$variable]) ? 'X' : '') . '</td>
                      <td>' . (isset($typehints[$variable]) ? $this->toHtmlList($typehints[$variable]) : '') . '</td>
                      <td>' . (isset($defaults[$variable]) ? $this->toHtmlList($defaults[$variable]) : '') . '</td>
                  </tr>';
        }
        $html = implode(PHP_EOL, $html);

        $finalHTML = $this->injectBloc($finalHTML, 'ANALYZERS', $html);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'CONTENT', '');
        $this->putBasedPage($section->file, $finalHTML);
    }

    protected function generateFossilizedMethods(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        // List of extensions used
        $res = $this->dump->fetchHashResults('FossilizedMethods');
        if ($res->isEmpty()) {
            $this->emptyResult($section);

            return ;
        }

        $html = array();
        $data = array();
        foreach ($res->order(function (array $a, array $b): int { return $b['value'] <=> $a['value']; })->toArray() as $value) {
            $data[$value['key'] . ' level' . ($value['key'] == 1 ? '' : 's')] = $value['value'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $html);

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => 'Fossilized Methods', 'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS', $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'TYPE', 'Methods');

        $this->putBasedPage($section->file, $finalHTML);
    }

    private function generateClassDepth(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        // List of extensions used
        $res = $this->dump->fetchHashResults('Class Depth');
        if ($res->isEmpty()) {
            $this->emptyResult($section);

            return ;
        }

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
                $data[$value['key'] . ' level' . ($value['key'] == 1 ? '' : 's')] = $value['value'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $html);

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => 'Depth', 'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'TYPE', 'Class');

        $this->putBasedPage($section->file, $finalHTML);
    }

    private function generateClassSize(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        // List of extensions used
        $res = $this->dump->getCitBySize();

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            if (count($data) < 50) {
                $data[$value['name']] = $value['size'];
            }

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['name'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['size'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $html);

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => 'Class size (lines)', 'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'TYPE', 'Class');

        $this->putBasedPage($section->file, $finalHTML);
    }

    private function generateTypehintStats(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        // List of extensions used
        $res = $this->dump->fetchHashResults('Typehinting stats');

        $data = array('object' => 0);
        $total = 0;
        foreach ($res->toArray() as $value) {
            if (in_array($value['key'], array('totalArguments',
                                              'totalFunctions', ))) {
                $total += (int) $value['value'];
                continue;
            }

            if (in_array($value['key'], array('\\array',
                                               '\callable',
                                               '\\int',
                                               '\\string',
                                               '\\void',
                                               '\\iterable',
                                               '\\bool',
                                               '\\float',
                                              ), \STRICT_COMPARISON)) {
                $data[$value['key']] = $value['value'];
                continue;
            }

        if (strpos($value['key'], '\\') !== false) {
            $data['object'] += $value['value'];
        }

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }

        $data['no type'] = $total - array_sum($data);
        arsort($data);

        $html = array();
        foreach($data as $name => $value) {
            $html []= <<<HTML
<div class="clearfix">
    <div class="block-cell-name">$name</div>
    <div class="block-cell-issue text-center">$value</div>
</div>

HTML;
        }

        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', implode(PHP_EOL, $html));

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => 'Typehint stats',
                                    'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'TYPE', 'Class');

        $this->putBasedPage($section->file, $finalHTML);
    }

    private function generateMethodSize(Section $section): void {
        $finalHTML = $this->getBasedPage($section->source);

        // List of extensions used
        $res = $this->dump->getMethodsBySize();
        $res->order(function (array $a, array $b): int { return $b['size'] <=> $a['size'];});

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            if (count($data) < 30) {
                $data[$value['name']] = $value['size'];
            }

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['name'] . '</div>
                      <div class="block-cell-issue text-center">' . $value['size'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $finalHTML = $this->injectBloc($finalHTML, 'TOPFILE', $html);

        $highchart = new Highchart();
        $highchart->addSeries('filename',
                              array_keys($data),
                              array('name' => 'Method size', 'data' => array_values($data))
                              );
        $blocjs = (string) $highchart;

        $finalHTML = $this->injectBloc($finalHTML, 'BLOC-JS',  $blocjs);
        $finalHTML = $this->injectBloc($finalHTML, 'TITLE', $section->title);
        $finalHTML = $this->injectBloc($finalHTML, 'TYPE', 'Method');

        $this->putBasedPage($section->file, $finalHTML);
    }

    private function generateStats(Section $section): void {
        $results = new Stats();
        $report = $results->generate('', self::INLINE);
        $report = json_decode($report);

        $stats = array();
        foreach($report as $group => $hash) {
            $stats []= "<tr><td colspan=2 bgcolor=\"#BBB\">$group</td></tr>";

            foreach($hash as $name => $count) {
                $stats []= "<tr><td>$name</td><td>$count</td></tr>";
            }
        }
        $stats = implode(PHP_EOL, $stats);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'STATS', $stats);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->source, $html);
    }

    private function generateComplexExpressions(Section $section): void {
        $results = $this->dump->fetchAnalysers(array('Structures/ComplexExpression'), array('phpsyntax' => array('fullcode' => 'htmlcode')));

        $expr = $results->getColumn('fullcode');
        $counts = array_count_values($expr);

        $expressions = array();
        foreach($results->toArray() as $row) {
            $expressions []= "<tr><td>{$row['file']}:{$row['line']}</td><td>{$counts[$row['fullcode']]}</td><td>{$row['htmlcode']}</td></tr>";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'BLOC-EXPRESSIONS', implode(PHP_EOL, $expressions));
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->source, $html);
    }

    protected function generateCodes(Section $section): void {
        $path = "{$this->tmpName}/data/sources";
        $pathToSource = dirname($this->tmpName) . '/code';
        mkdir($path, 0755);

        $filesList = $this->dump->fetchTable('files')->toArray();
        $files = '';
        foreach($filesList as $row) {
            if (!file_exists($path . '/' . dirname($row['file']))) {
                mkdir($path . '/' . dirname($row['file']), 0755, true);
            }

            $sourcePath = "$pathToSource$row[file]";
            if (!file_exists($sourcePath)) {
                continue;
            }

            $id = str_replace('/', '_', $row['file']);
            $source = @highlight_file($sourcePath, \RETURN_VALUE);
            $files .= '<li><a href="#" id="' . $id . '" class="menuitem">' . makeHtml($row['file']) . "</a></li>\n";
            $source = substr($source, 6, -8);
            $source = preg_replace_callback('#<br />#is', function (): string {
                static $i = 0;

                return '<br /><a name="l' . ++$i . '" />';
            }, $source);
            file_put_contents("$path$row[file]", $source);
        }

        $blocjs = <<<'JAVASCRIPT'
<script>
  "use strict";

  $('.menuitem').click(function(event){
    $('#results').load("sources/" + event.target.text);
    $('#filename').html(event.target.text + '  <span class="caret"></span>');
  });

  var fileParam = window.location.hash.split('file=')[1];
  if(fileParam !== undefined) {
    var limit = fileParam.indexOf('&');
    if (limit !== -1) {
        fileParam = fileParam.substr(0, limit);
    }
    $('#results').load("sources/" + fileParam);
    $('#filename').html(fileParam + '  <span class="caret"></span>');
  }

  var line = window.location.hash.split('line=')[1];
  if(line !== undefined) {
        window.location.hash = 'l' + line;
  }
  
  </script>
JAVASCRIPT;
        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'BLOC-JS', $blocjs);
        $html = $this->injectBloc($html, 'FILES', $files);
        $html = $this->injectBloc($html, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $html);
    }

    private function generateFileDependencies(Section $section): void {
        $res = $this->dump->fetchTable('filesDependencies');
        $res->filter(function (array $x): bool { return ($x['included'] !== $x['including']) && in_array($x['type'], array('IMPLEMENTS', 'EXTENDS', 'INCLUDE', 'NEW'), \STRICT_COMPARISON);});

        $nodes = array();
        foreach($res->toArray() as $row) {
            if (isset($nodes[$row['including']][$row['included']])) {
                $nodes[$row['including']][$row['included']] .= ', ' . $row['type'];
            } else {
                $nodes[$row['including']][$row['included']] = $row['type'];
            }
        }

        $next = array();
        foreach($nodes as $in => $out) {
            $set = false;

            foreach($next as $file => &$inc) {
                if ($file === $in) {
                    $inc = $out;
                    $set = true;
                }
            }
            unset($inc);

            if ($set === false) {
                $next[$in] = $out;
            }
        }
        unset($out);

        foreach($next as $in => &$out) {
            $out = array_keys($out);
            sort($out);
        }
        unset($out);

        if (empty($next)) {
            $secondaries = array();
        } else {
            $secondaries = array_merge(...array_values($next));
        }
        $top = array_diff(array_keys($next), $secondaries);

        $theTable = array();
        foreach($top as $t) {
            $theTable[] = '<ul class="tree">' . $this->extends2ul($t, $next) . '</ul>';
        }
        $theTable = implode(PHP_EOL, $theTable);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'This is the list of file dependencies. The top files require the bottom files to be included to be properly running. This dependency tree covers class usage : new, ::, extends and implements.');
        $html = $this->injectBloc($html, 'CONTENT', $theTable);
        $this->putBasedPage($section->file, $html);
    }

    private function generateIdenticalFiles(Section $section): void {
        $res = $this->dump->getIdenticalFiles();

        $theTable = array();
        foreach($res->toArray() as $row) {
            $list = str_replace(',', "</li>\n<li>", $row['list']);

            $theTable[] = <<<HTML
<tr>
    <td>$row[count]</td>
    <td>
        <ul>
            <li>$list</li>
        </ul>
    </td>
</tr>
HTML;
        }
        $theTable = implode(PHP_EOL, $theTable);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'IDENTICAL', $theTable);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateConcentratedIssues(Section $section): void {
        $list = $this->rulesets->getRulesetsAnalyzers(array('Analyze'));

        $res = $this->dump->getConcentratedIssues($list);

        $table = array();
        foreach($res->toArray() as list('line' => $line, 'file' => $file, 'count' => $count, 'list' => $list)) {
            $listHtml = array();
            foreach(explode(',', $list) as $l) {
                $listHtml[] = '<li>' . $this->makeDocLink($l) . '</li>';
            }
            $listHtml = '<ul>' . implode(PHP_EOL, $listHtml) . '</ul>';
            $table[] = "<tr><td>$file:$line</td><td>$count</td><td>$listHtml</td></tr>\n";
        }

        $table = implode(PHP_EOL, $table);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'BLOC-EXPRESSIONS', $table);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    private function generateConfusingVariables(Section $section): void {
        $data = new Data\CloseNaming($this->dump);
        $results = $data->prepare();
        $reasons = array('_'       => 'One _',
                         'numbers' => 'One digit',
                         'swap'    => 'Partial inversion',
                         'one'     => 'One letter',
                         'case'    => 'Case',
                         );

        $table = array();
        foreach($results as $variable => $close) {
            $confused = array();

            foreach($close as $reason => $variables) {
                $list = '<ul><li>' . implode('</li><li>', $variables) . "</li></ul>\n";
                $confused[] = "<tr><td>$list</td><td>{$reasons[$reason]}</td></tr>\n";
            }

            $count = count($close);
            $first = array_shift($confused);
            $table[] = str_replace('<tr>', "<tr><td rowspan=\"$count\">$variable</td>", $first) . PHP_EOL . implode('', $confused);
        }
        $table = implode(PHP_EOL, $table);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'CONTENT', $table);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function makeIcon(string $tag): string {
        switch($tag) {
            case self::YES :
                return '<i class="fa fa-check-square-o"></i>';
            case self::NO :
                return '<i class="fa fa-square-o"></i>';
            case self::NOT_RUN :
                return '<i class="fa fa-ban"></i>';
            case self::INCOMPATIBLE :
                return '<i class="fa fa-remove"></i>';
            default :
                return '&nbsp;';
        }
    }

    private function Bugfixes_cve(string $cve): string {
        if (empty($cve)) {
            return '-';
        }

        if (strpos($cve, ', ') === false) {
            $cveHtml = '<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=' . $cve . '">' . $cve . '</a>';
        } else {
            $cves = explode(', ', $cve);
            $cveHtml = array();
            foreach($cves as $cve) {
                $cveHtml[] = '<a href="https://cve.mitre.org/cgi-bin/cvename.cgi?name=' . $cve . '">' . $cve . '</a>';
            }
            $cveHtml = implode(',<br />', $cveHtml);
        }

        return $cveHtml;
    }

    protected function Compatibility(int $count, string $analyzer): string {
        if ($count === Analyzer::VERSION_INCOMPATIBLE) {
            return '<i class="fa fa-ban" style="color: orange"></i>';
        } elseif ($count === Analyzer::CONFIGURATION_INCOMPATIBLE) {
            return '<i class="fa fa-cogs" style="color: orange"></i>';
        } elseif ($count === 0) {
            return '<i class="fa fa-check-square-o" style="color: green"></i>';
        } else {
            return '<i class="fa fa-warning" style="color: red"></i>&nbsp;<a href="compatibility_issues.html#analyzer=' . $this->toId($analyzer) . '">' . $count . ' warnings</a>';
        }
    }

    protected function toId(string $name): string {
        return str_replace(array('/', '*', '(', ')', '.'), '_', strtolower($name));
    }

    protected function toOnlineId(string $name): string {
        return str_replace(array(' ', '(', ')', '/'), '-', strtolower($name));
    }

    protected function makeAuditDate(string &$finalHTML): void {
        $audit_date = 'Audit date : ' . date('d-m-Y h:i:s', time());
        $audit_name = $this->dump->fetchHash('audit_name')->toString();
        if (!empty($audit_name)) {
            $audit_date .= " - &quot;$audit_name&quot;";
        }

        $exakat_version = $this->dump->fetchHash('exakat_version')->toString();
        $exakat_build = $this->dump->fetchHash('exakat_build')->toString();
        $audit_date .= " - Exakat $exakat_version ($exakat_build)";
        $finalHTML = $this->injectBloc($finalHTML, 'AUDIT_DATE', $audit_date);
    }

    protected function getVCSInfo(): array {
        $info = array();

        $vcsClass = Vcs::getVcs($this->config);
        $vcsName = explode('\\', $vcsClass);
        $vcsName = array_pop($vcsName);
        switch($vcsName) {
            case 'Git':
                $info[] = array('Git URL', $this->dump->fetchHash('vcs_url')->toString());

                $res = $this->dump->fetchHash('vcs_branch')->toString();
                if (!empty($res)) {
                    $info[] = array('Git branch', trim($res));
                }

                $res = $this->dump->fetchHash('vcs_revision')->toString();
                if (!empty($res)) {
                    $info[] = array('Git commit', trim($res));
                }
                break 1;

            case 'Svn':
                $info[] = array('SVN URL', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Bazaar':
                $info[] = array('Bazaar URL', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Composer':
                $info[] = array('Package', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Mercurial':
                $info[] = array('Hg URL', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Copy':
                $info[] = array('Original path', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Symlink':
                $info[] = array('Original path', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Tarbz':
                $info[] = array('Source URL', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            case 'Targz':
                $info[] = array('Source URL', $this->dump->fetchHash('vcs_url')->toString());
                break 1;

            default :
                $info[] = array('Repository URL', 'Downloaded archive');
        }

        return $info;
    }

    protected function makeDocLink(string $analyzer): string {
        $docs = $this->docs->getDocs($analyzer, 'name');
        assert(!is_array($docs), "Missing docs('name') for $analyzer");
        return "<a href=\"analyses_doc.html#{$this->toId($analyzer)}\" id=\"{$this->toId($analyzer)}\"><i class=\"fa fa-book\" style=\"font-size: 14px\"></i></a> &nbsp; $docs";
    }

    protected function toHtmlList(array $array): string {
        return '<ul><li>' . implode("</li>\n<li>", $array) . '</li></ul>';
    }

    protected function getTotalAnalyzer(): array {
        return $this->dump->getTotalAnalyzer();
    }

    protected function generateAppinfo(Section $section): void {
        $data = new Data\Appinfo($this->dump);
        $data->prepare();

        $list = array();
        $originals = $data->originals();
        foreach($data->values() as $group => $points) {
            $listPoint = array();
            foreach($points as $point => $status) {

                if (isset($originals[$group][$point], $this->frequences[$originals[$group][$point]])) {
                    $percentage = $this->frequences[$originals[$group][$point]];
                    $percentageDisplay = "$percentage %";
                } else {
                    $percentage = 0;
                    $percentageDisplay = '&nbsp;';
                }

                $statusIcon = $this->makeIcon($status);
                $htmlPoint = makeHtml($point);
                $listPoint[] = <<<HTML
<li><div style="width: 90%; text-align: left;display: inline-block;">$statusIcon&nbsp;$htmlPoint&nbsp;</div><div style="display: inline-block; width: 10%;"><span class="progress progress-sm"><div class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="20" aria-valuemin="0" aria-valuemax="100" style="width: $percentage%; color:black;">$percentageDisplay</div><div>&nbsp;</div></span></li>
HTML;
            }

            $listPoint = implode(PHP_EOL, $listPoint);
            $list[] = <<<HTML
        <ul class="sidebar-menu">
          <li class="treeview">
            <a href="#"><i class="fa fa-certificate"></i> <span>$group</span><i class="fa fa-angle-left pull-right"></i></a>
            <ul class="treeview-menu">
                $listPoint
            </ul>
          </li>
        </ul>
HTML;
        }

        $list = implode("\n", $list);
        $list = <<<HTML
        <div class="sidebar">
$list
        </div>
HTML;

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'APPINFO', $list);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

    protected function generateInventoriesEncoding(Section $section): void {
        // List of indentation used
        $res = $this->dump->fetchHashResults('Mbstring Encodings');
        if ($res->isEmpty()) {
            $this->emptyResult($section);

            return ;
        }

        $values = $res->toHash('key', 'value');
        asort($values);

        $theTable = array();
        foreach($values as $encoding => $count) {
            $codeHtml = PHPSyntax($encoding);
            $theTable []= "<tr><td>{$codeHtml}</td><td>$count</td><td>&nbsp</td></tr>";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Names of the encoding used in the code');
        $html = $this->injectBloc($html, 'TABLE', implode(PHP_EOL, $theTable));
        $this->putBasedPage($section->file, $html);
    }

    protected function generateInventoriesOpenSSLCiphers(Section $section): void {
        // List of indentation used
        $res = $this->dump->fetchHashResults('OpenSSL Ciphers');

        $values = $res->toHash('key', 'value');
        asort($values);

        $theTable = array();
        foreach($values as $encoding => $count) {
            $codeHtml = PHPSyntax($encoding);
            $theTable []= "<tr><td>{$codeHtml}</td><td>$count</td><td>&nbsp</td></tr>";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'List of all the OpenSSL cipher used in the code.');
        $html = $this->injectBloc($html, 'TABLE', implode(PHP_EOL, $theTable));
        $this->putBasedPage($section->file, $html);
    }

    protected function generateInventoriesProtocols(Section $section): void {
        // List of indentation used
        $res = $this->dump->fetchHashResults('Protocols');

        $values = $res->toHash('key', 'value');
        asort($values);

        $theTable = array();
        foreach($values as $encoding => $count) {
            $codeHtml = PHPSyntax($encoding);
            $theTable []= "<tr><td>{$codeHtml}</td><td>$count</td><td>&nbsp</td></tr>";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'List of all PHP protocols used in the code.');
        $html = $this->injectBloc($html, 'TABLE', implode(PHP_EOL, $theTable));
        $this->putBasedPage($section->file, $html);
    }

    protected function generateFixesRector(Section $section): void {
        $rector = new Rector();
        $report = $rector->generate('', self::INLINE);

        $configline = trim($report);
        $configline = str_replace(array(' ', "\n") , array('&nbsp;', "<br />\n", ), $configline);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'COMPILATION', $configline);
        $html = $this->injectBloc($html, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $html);
    }

    protected function generateFixesPhpCsFixer(Section $section): void {
        $phpcsfixer = new Phpcsfixer();
        $report = $phpcsfixer->generate('', self::INLINE);

        $configline = trim($report);
        $configline = PHPSyntax($configline);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'COMPILATION', $configline);
        $html = $this->injectBloc($html, 'TITLE', $section->title);

        $this->putBasedPage($section->file, $html);
    }

    protected function generateIndentationLevelsBreakdown(Section $section): void {
        // List of indentation used
        $res = $this->dump->fetchHashResults('Dump/IndentationLevels');
        if ($res->isEmpty()) {
            $this->emptyResult($section);

            return ;
        }

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            $data[$value['key'] . ' level '] = (int) $value['value'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . ' levels</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Indentation levels', $data, $html);
    }

    private function generateTypehintSuggestions(Section $section): void {
//        $constants  = $this->generateVisibilityConstantSuggestions();
//        $properties = $this->generateVisibilityPropertySuggestions();
        $methods    = $this->generateTypehintMethodsSuggestions();

        $classes = array_unique(array_merge(array_keys($methods)));

        $headers = <<<'HTML'
    <tr>
        <td>&nbsp;</td>
        <td>Method</td>
        <td>Argument</td>
        <td>Typehint</td>
        <td>Default</td>
    </tr>
HTML;

        $visibilityTable = array('<table class="table table-striped">',
                                 $headers,
                                 );

        foreach($classes as $id) {
            list(, $class) = explode(':', $id, 3);
            $visibilityTable []= '<tr><td colspan="9">' . PHPSyntax($class) . '</td></tr>' . PHP_EOL .
                                $headers . PHP_EOL .
//                                (isset($constants[$id])  ? implode('', $constants[$id])  : '') .
//                                (isset($properties[$id]) ? implode('', $properties[$id]) : '') .
                                (isset($methods[$id]) ? implode('', $methods[$id]) : '');
        }

        $visibilityTable []= '</table>';
        $visibilityTable = implode(PHP_EOL, $visibilityTable);

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'Below is a summary of all classes and their parameters\'s typehinting status. ');
        $html = $this->injectBloc($html, 'CONTENT', $visibilityTable);
        $this->putBasedPage($section->file, $html);
    }

    protected function generateDereferencingLevelsBreakdown(Section $section): void {
        // List of indentation used
        $res = $this->dump->fetchHashResults('Dump/DereferencingLevels');
        if ($res->isEmpty()) {
            $this->emptyResult($section);
            return ;
        }

        $html = array();
        $data = array();
        foreach ($res->toArray() as $value) {
            $data["'{$value['key']} level'"] = (int) $value['value'];

            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $value['key'] . ' levels</div>
                      <div class="block-cell-issue text-center">' . $value['value'] . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Dereferencing levels', $data, $html);
    }

    private function generateTypehintMethodsSuggestions(): array {
        $res = $this->dump->fetchTableMethodsByArgument();
        $arguments = array();
        foreach($res->toArray() as $row) {
            $theMethod = $row['fullnspath'];
            $visibilities = array($row['typehint'], $row['init']);

            $argument = '<tr><td>&nbsp;</td><td>&nbsp;</td><td>' . PHPSyntax($row['argument'] ?? '') . '</td><td class="exakat_short_text">' .
                                    implode('</td><td>', $visibilities)
                                 . '</td></tr>' . PHP_EOL;

            array_collect_by($arguments, $theMethod, $argument);
        }

        $return = array();
        $res = $this->dump->fetchTableMethodsByReturntype();
        foreach($res->toArray() as $row) {
            $visibilities = array($row['returntype'], '&nbsp;');

            $method = '<tr><td>&nbsp;</td><td>' . PHPSyntax($row['method'] ?? '') . '</td><td>&nbsp;</td><td class="exakat_short_text">' .
                                    implode('</td><td>', $visibilities)
                                 . '</td></tr>' . PHP_EOL;
            $method .= implode(PHP_EOL, $arguments[$row['fullnspath'] . '::' . mb_strtolower($row['method'] ?? '')] ?? array());

            array_collect_by($return, $row['fullnspath'] . ':' . $row['theClass'], $method);
        }

        unset($return['']);

        return $return;
    }

    protected function generateForeachFavorites(Section $section): void {
        // List of indentation used
        $res = $this->dump->fetchHashResults('Foreach Names');
        $res->map(function (array $x): array { $x['key'] = str_replace('&', '', $x['key']); return $x; });

        // merging results from &$v and $v into one
        $data = array();
        foreach ($res->toArray() as $value) {
            $data[$value['key']] =
                (int) $value['value'] + ($data[$value['key']] ?? 0);
        }
        arsort($data);

        $html = array();
        foreach ($data as $key => $value) {
            $html []= '<div class="clearfix">
                      <div class="block-cell-name">' . $key . '</div>
                      <div class="block-cell-issue text-center">' . $value . '</div>
                  </div>';
        }
        $html = implode(PHP_EOL, $html);

        $this->generateGraphList($section->file, $section->title, 'Foreach names', $data, $html);
    }

    protected function generateUsedMagic(Section $section): void {
        $results = $this->dump->fetchAnalysers(array('Classes/MagicMethod',
                                                     'Classes/MagicProperties',
                                                     ));
        $results->load();

        $expr = $results->getColumn('fullcode');
        $expr = array_map(function (string $x): string { return trim($x, '{}');}, $expr);
        $counts = array_count_values($expr);

        $expressions = '';
        foreach($results->toArray() as $row) {
            $row['fullcode'] = trim($row['fullcode'], '{}');
            $fullcode = PHPSyntax($row['fullcode']);
            $expressions .= "<tr><td>{$row['file']}:{$row['line']}</td><td>{$counts[$row['fullcode']]}</td><td>$fullcode</td></tr>\n";
        }

        $html = $this->getBasedPage($section->source);
        $html = $this->injectBloc($html, 'TABLE', $expressions);
        $html = $this->injectBloc($html, 'DESCRIPTION', 'List of magic properties used in the code');
        $html = $this->injectBloc($html, 'TITLE', $section->title);
        $this->putBasedPage($section->file, $html);
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Autoload;

include __DIR__ . '/Autoloader.php';

class Autoload implements Autoloader {
    public function autoload($name) {
        $file = dirname(__DIR__, 2) . '/' . str_replace('\\', DIRECTORY_SEPARATOR, $name) . '.php';

        if (file_exists($file)) {
            include $file;
        }
    }

    public static function autoload_test($name) {
        $file = dirname(__DIR__, 3) . '/tests/analyzer/' . str_replace('\\', DIRECTORY_SEPARATOR, $name) . '.php';

        if (file_exists($file)) {
            include $file;
        }
    }

    public static function autoload_phpunit($name) {
        $fileName = preg_replace('/^([^_]+?)_(.*)$/', '$1' . DIRECTORY_SEPARATOR . '$2', $name);
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $fileName);

        $file = dirname(__DIR__, 3) . "/tests/analyzer/{$fileName}.php";
        if (file_exists($file)) {
            include $file;
            return;
        }

        $file = dirname(__DIR__, 3) . "/tests/cobbler/{$fileName}.php";
        if (file_exists($file)) {
            include $file;
            return;
        }
    }

    public function registerAutoload() {
        spl_autoload_register(array(self::class, 'autoload'));
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Autoload;

use Phar;

class AutoloadDev implements Autoloader {
    public const LOAD_ALL = null;

    private $path = '';

    public function __construct($path) {
        if (class_exists('\\Phar') && phar::running()) {
            // No autoloadDev with phar
            // Ignoring it all
            return;
        }

        $this->path = $path;
    }

    public function autoload($name): void {
        if (empty($this->path)) {
            return;
        }

        $fileName = str_replace(array('Exakat\\', '\\'), array('', DIRECTORY_SEPARATOR), $name) . '.php';

        if (file_exists("{$this->path}/$fileName")) {
            include "{$this->path}/$fileName";
        }
    }

    public function registerAutoload() {
        spl_autoload_register(array($this, 'autoload'));
    }

    public function getAllAnalyzers() {
        $fullPath = "{$this->path}/Analyzer/analyzers.ini";

        if (!file_exists($fullPath)) {
            return array();
        }

        $ini = parse_ini_file($fullPath);

        return $ini === false ? array() : $ini;
    }

    public function loadIni($name, $libel = self::LOAD_ALL) {
        $fullPath = "{$this->path}/data/$name";

        if (!file_exists($fullPath)) {
            return array();
        }

        $ini = parse_ini_file($fullPath, \INI_PROCESS_SECTIONS);
        if (empty($ini)) {
            return array();
        }

        if ($libel === self::LOAD_ALL) {
            $return = $ini;
        } else {
            $return = $ini[$libel];
        }

        return array_merge($return);
    }

    public function loadJson($name, $libel = self::LOAD_ALL) {
        $fullPath = "{$this->path}/data/$name";

        if (!file_exists($fullPath)) {
            return array();
        }

        $json = file_get_contents($fullPath);
        if (empty($json)) {
            return array();
        }

        $return = json_decode($json, true);
        if (empty($return)) {
            return array();
        }

        return $return;
    }

    public function loadData($path) {
        $fullPath = "{$this->path}/$path";

        if (file_exists($fullPath)) {
            return file_get_contents($fullPath);
        } else {
            return null;
        }
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Autoload;


class AutoloadExt implements Autoloader {
    public const LOAD_ALL = null;

    private $pharList   = array();
    private $extensions = array();

    public function __construct($path) {
        if (!extension_loaded('phar')) {
            // Ignoring it all
            return;
        }
        $list = glob("$path/*.phar", GLOB_NOSORT);

        foreach($list as $phar) {
            $this->pharList[basename($phar, '.phar')] = $phar;
        }

        // Add a list of check on the phars
        // Could we autoload everything ?
    }

    public function autoload($name) {
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $name);
        $file = "{$fileName}.php";

        foreach($this->pharList as $phar) {
            $fullPath = "phar://$phar/$file";
            if (file_exists($fullPath)) {
                include $fullPath;
                return;
            }
        }
    }

    public function registerAutoload() {
        spl_autoload_register(array($this, 'autoload'));
    }

    private function checkDependencies() {
        // Report missing extensions, but don't prevent them (some rules may still work, others will be ignored)
        foreach($this->extensions as $name => $extension) {
            $diff = array_diff($extension->dependsOnExtensions(), array_keys($this->pharList));
            if (!empty($diff)) {
                // This is displayed for extensions and also for their dependencies, leading to repetition.
                display("$name extension requires the following missing extension : " . implode(', ', $diff) . "\nProcessing may be impacted.\nDownload the missing extensions with the 'extension' command.\n");
            }
         }
    }

    public function getPharList() {
        return array_map('basename', $this->pharList);
    }

    public function getRulesets() {
        $return = array();

        foreach($this->pharList as $name => $phar) {
            $fullPath = "phar://$phar/Exakat/Analyzer/analyzers.ini";

            if (!file_exists($fullPath)) {
                $return[] = array();
                continue;
            }
            $ini = parse_ini_file($fullPath);
            unset($ini['All']); // And other pre-defined themes ?

            $return[$name] = array_keys($ini);
        }

        return $return;
    }

    public function getAnalyzers(string $theme = 'All') {
        $return = array();

        foreach($this->pharList as $name => $phar) {
            $fullPath = "phar://$phar/Exakat/Analyzer/analyzers.ini";

            if (!file_exists($fullPath)) {
                $return[] = array();
                continue;
            }
            $ini = parse_ini_file($fullPath);

            $return[$name] = $ini[$theme] ?? array();
        }

        return $return;
    }

    public function getAllAnalyzers() {
        $return = array();

        foreach($this->pharList as $name => $phar) {
            $fullPath = "phar://$phar/Exakat/Analyzer/analyzers.ini";

            if (!file_exists($fullPath)) {
                display("Missing analyzers.ini in $name\n");
                $return[] = array();
                continue;
            }
            $ini = parse_ini_file($fullPath);

            $return[$name] = $ini;
        }

        return $return;
    }

    public function loadIni($name, $libel = self::LOAD_ALL) {
        $return = array();

        foreach($this->pharList as $phar) {
            $fullPath = "phar://$phar/data/$name";

            if (!file_exists($fullPath)) {
                continue;
            }

            $ini = parse_ini_file($fullPath, \INI_PROCESS_SECTIONS);
            if (empty($ini)) {
                continue;
            }

            if ($libel === self::LOAD_ALL) {
                $return[] = $ini;
            } else {
                $return[] = $ini[$libel];
            }
        }

        if (empty($return)) {
            return array();
        }

        return array_merge(...$return);
    }

    public function loadJson($name, $libel = self::LOAD_ALL) {
        $return = array(array());

        foreach($this->pharList as $phar) {
            $fullPath = "phar://$phar/data/$name";

            if (!file_exists($fullPath)) {
                continue;
            }

            $json = file_get_contents($fullPath);
            if (empty($json)) {
                continue;
            }

            $data = json_decode($json, \JSON_ASSOCIATIVE);

            if(json_last_error() !== \JSON_ERROR_NONE) {
                continue;
            }
            if (empty($data)) {
                continue;
            }

            if ($libel === self::LOAD_ALL) {
                $return[] = array_column($data, $libel);
            } else {
                $return[] = $data;
            }
        }

        return array_merge(...$return);
    }

    public function loadData($path) {
        $return = array();
        foreach($this->pharList as $phar) {
            $fullPath = "phar://$phar/$path";

            if (file_exists($fullPath)) {
                $return[] = file_get_contents($fullPath);
            }
        }

        return implode('', $return);
    }

    public function fileExists($path) {
        foreach($this->pharList as $phar) {
            $fullPath = "phar://$phar/$path";

            if (file_exists($fullPath)) {
                return true;
            }
        }

        return false;
    }

    public function copyFile($path, $to) {
        foreach($this->pharList as $phar) {
            $fullPath = "phar://$phar/$path";

            if (file_exists($fullPath)) {
                copy($fullPath, $to);
            }
        }

        return null;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Autoload;

interface Autoloader {
    public function autoload($name);

    public function registerAutoload();

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class CreateMagicMethod extends Complete {
    public function dependsOn(): array {
        return array('Complete/OverwrittenMethods',
                     'Complete/SetParentDefinition',
                     'Complete/SetClassRemoteDefinitionWithTypehint',
                    );
    }

    public function analyze(): void {

        // Missing : typehinted properties, return typehint, clone

        // link to __call
        $this->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
              ->outIs('OBJECT')
              // Others are possible too : $a[1], $b->c, D::$a
             ->atomIs(array('Variableobject', 'This'), self::WITHOUT_CONSTANTS)

             // For variables
             ->optional(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Parametername', self::WITHOUT_CONSTANTS)
                     ->inIs('NAME')
                     ->outIs('TYPEHINT')
             )

              ->inIs('DEFINITION')
              ->atomIs('Class', self::WITHOUT_CONSTANTS)
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs('MAGICMETHOD')
              ->outIs('NAME')
              ->codeIs('__call', self::TRANSLATE, self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();

        // link to __callStatic
        $this->atomIs('Staticmethodcall', self::WITHOUT_CONSTANTS)
             ->hasNoIn('DEFINITION')
             ->outIs('CLASS')
             ->atomIs(array('Variable', 'This', 'Nsname', 'Identifier', 'Self', 'Parent', 'Static'), self::WITHOUT_CONSTANTS)

             // For variables
             ->optional(
                $this->side()
                     ->inIs('DEFINITION')
                     ->atomIs('Parametername', self::WITHOUT_CONSTANTS)
                     ->inIs('NAME')
                     ->outIs('TYPEHINT')
             )

              ->inIs('DEFINITION')
              ->goToAllParentsTraits(self::INCLUDE_SELF)
              ->outIs('MAGICMETHOD')
              ->outIs('NAME')
              ->codeIs('__callstatic', self::TRANSLATE, self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SolveTraitMethods extends Complete {
    public function analyze(): void {
        // use a,b {m as c};
        // use a,b {a::m as c};
        // use a\d\e,b {a\d\e::m as c};
        $this->atomIs('Usetrait', self::WITHOUT_CONSTANTS)
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs('As', self::WITHOUT_CONSTANTS)

              ->outIs('AS')
              ->atomIs('Identifier', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'methode_target')
              ->inIs('AS')

              ->outIs('NAME')
              ->atomIs('Nsname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'methode')
              ->back('first')
              ->outIs('USE')
              ->inIs('DEFINITION')
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'methode', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('definition')

              ->back('first')
              ->inIs('USE')
              ->atomIs(array('Class', 'Trait'))
              ->outIs('METHOD')
              ->outIs('DEFINITION')
              ->as('results')
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'methode_target', self::CASE_INSENSITIVE)

              ->back('results')
              ->addEFrom('DEFINITION', 'definition');
        $this->prepareQuery();

        // use a,b {a::m insteadof c};
        $this->atomIs('Usetrait', self::WITHOUT_CONSTANTS)
              ->outIs('BLOCK')
              ->outIs('EXPRESSION')
              ->atomIs('Insteadof', self::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->outIs('METHOD')
              ->savePropertyAs('lccode', 'methode')
              ->inIs('METHOD')
              ->outIs('CLASS')
              ->inIs('DEFINITION')
              ->atomIs('Trait')
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'methode', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->as('definition')

              ->back('first')
              ->inIs('USE')
              ->atomIs(array('Class', 'Trait'))
              ->goToAllChildren(self::INCLUDE_SELF)
              ->outIs('METHOD')
              ->outIs('DEFINITION')
              ->as('results')
              ->outIs('METHOD')
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'methode')

              ->back('results')
              ->addEFrom('DEFINITION', 'definition');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\SavePropertyAs;

class VariableTypehint extends Analyzer {
    public function analyze(): void {
        // extends to global variables

        // adding integer typehint
        $this->atomIs('Variabledefinition')
             ->hasNoOut('TYPEHINT')
             // only one default
             ->filter(
                $this->side()
                     ->outIs('DEFAULT')
                     ->raw('count().is(eq(1))')
             )

             ->outIs('DEFAULT')
             ->atomIs(array('Integer', 'Null', 'String', 'Magicconstant', 'Heredoc', 'Arrayliteral', 'Boolean', 'Float'), self::WITH_CONSTANTS)
             ->savePropertyAs('label', 'atomValue')
             ->back('first')
             ->initVariable('fnp', "''")
             ->raw(<<<'GREMLIN'
        sideEffect{ 
            fnp = "DEFAULT VALUE";
            switch(atomValue) {
                case 'Integer'       : fnp = "\\int";        break;
                case 'Null'          : fnp = "\\null";       break;
                case 'String'        : fnp = "\\string";     break;
                case 'Heredoc'       : fnp = "\\string";     break;
                case 'Magicconstant' : fnp = "\\string";     break;
                case 'Arrayliteral'  : fnp = "\\array";      break;
                case 'Boolean'       : fnp = "\\bool";       break;
                default : 
                    fnp = "DEFAULT TYPE";break;
            }
        }
GREMLIN
)
             ->addAtom('Scalartypehint', array(
                'fullcode'   => 'fnp',
                'fullnspath' => 'fnp',
                'ws'         => '{"closing":" "}',
                'rank'       => 0,
             ))

             ->addEFrom('TYPEHINT', 'first')
             ->back('first');
        $this->prepareQuery();

        // adding returned type
        $this->atomIs('Variabledefinition')
             ->hasNoOut('TYPEHINT')
             // only one default
             ->filter(
                $this->side()
                     ->outIs('DEFAULT')
                     ->raw('count().is(eq(1))')
             )

             ->outIs('DEFAULT')
             ->atomIs(array('Functioncall', 'Methodcall', 'Staticmethodcall'), self::WITH_CONSTANTS)
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')
             ->duplicateNode()
             ->addEFrom('TYPEHINT', 'first')
             ->back('first');
        $this->prepareQuery();

        // adding new x() with class definition
        $this->atomIs('Variabledefinition')
             ->hasNoOut('TYPEHINT')
             // only one default
             // could be upgraded to multiple identical new
             ->filter(
                $this->side()
                     ->outIs('DEFAULT')
                     ->raw('count().is(eq(1))')
             )

             ->outIs('DEFAULT')
             ->atomIs(array('New'), self::WITH_CONSTANTS)
             ->outIs('NEW')
             ->inIs('DEFINITION')
             ->as('theClass')
             ->has('fullnspath')
             ->savePropertyAs('whole', 'definition')
             ->back('first')
             ->addAtom('Identifier', array(
                'ws'         => '{"closing":" "}',
                'rank'       => 0,
                'line'       => 0,
             ))
             ->raw(<<<'GREMLIN'
sideEffect{
    it.get().property("fullcode",   definition.value("fullnspath"));
    it.get().property("fullnspath", definition.value("fullnspath"));

    if (definition.property("isPhp").any())  { it.get().property("isPhp", true); }
    if (definition.property("isExt").any())  { it.get().property("isExt", true); }
    if (definition.property("isStub").any()) { it.get().property("isStub", true); }
}
GREMLIN
)
             ->as('typehint')
             ->addEFrom('TYPEHINT', 'first')
             ->back('typehint')
             ->addEFrom('DEFINITION', 'theClass')
             ->back('first');
        $this->prepareQuery();

        // adding new stdclass()
        $this->atomIs('Variabledefinition')
             ->hasNoOut('TYPEHINT')
             // only one default
             // could be upgraded to multiple identical new
             ->filter(
                $this->side()
                     ->outIs('DEFAULT')
                     ->raw('count().is(eq(1))')
             )

             ->outIs('DEFAULT')
             ->atomIs(array('New'), self::WITH_CONSTANTS)
             ->outIs('NEW')
             ->hasNoIn('DEFINITION')
             ->has('fullnspath')
             ->savePropertyAs('whole', 'definition')
             ->back('first')
             ->addAtom('Identifier', array(
                'ws'         => '{"closing":" "}',
                'rank'       => 0,
                'line'       => 0,
             ))
             ->raw(<<<'GREMLIN'
sideEffect{
    it.get().property("fullcode",   definition.value("fullnspath"));
    it.get().property("fullnspath", definition.value("fullnspath"));

    if (definition.property("isPhp").any()) { it.get().property("isPhp", true); }
    if (definition.property("isExt").any()) { it.get().property("isExt", true); }
    if (definition.property("isStub").any()) { it.get().property("isStub", true); }
}
GREMLIN
)
             ->addEFrom('TYPEHINT', 'first')
             ->back('first');
        $this->prepareQuery();

        // adding new stdclass()
        $this->atomIs('Variabledefinition')
             ->hasNoOut('TYPEHINT')
             // only one default
             // could be upgraded to multiple identical new
             ->filter(
                $this->side()
                     ->outIs('DEFAULT')
                     ->raw('count().is(eq(1))')
             )

             ->outIs('DEFAULT')
             ->atomIs(self::FUNCTIONS_CALLS, self::WITH_CONSTANTS)
             ->inIs('DEFINITION')
             // Method, functions, etc.
             ->outIs('RETURNTYPE')
             ->inIs('DEFINITION')
             ->has('fullnspath')
             ->savePropertyAs('whole', 'definition')
             ->back('first')
             ->addAtom('Identifier', array(
                'ws'         => '{"closing":" "}',
                'rank'       => 0,
                'line'       => 0,
             ))
             ->raw(<<<'GREMLIN'
sideEffect{
    it.get().property("fullcode",   definition.value("fullnspath"));
    it.get().property("fullnspath", definition.value("fullnspath"));

    if (definition.property("isPhp").any()) { it.get().property("isPhp", true); }
    if (definition.property("isExt").any()) { it.get().property("isExt", true); }
    if (definition.property("isStub").any()) { it.get().property("isStub", true); }
}
GREMLIN
)
             ->addEFrom('TYPEHINT', 'first')
             ->back('first');
        $this->prepareQuery();

        // adding type via catch()
        $this->atomIs('Variabledefinition')
             ->hasNoOut('TYPEHINT')

             ->outIs('DEFINITION')
             ->inIs('VARIABLE')
             ->atomIs('Catch')
             ->outIs('CLASS') // all of them
             ->savePropertyAs(SavePropertyAs::ATOM, 'definition')
             ->back('first')
             ->addAtom('Identifier', array(
                'ws'         => '{"closing":" "}',
                'rank'       => 0,
                'line'       => 0,
             ))
             ->raw(<<<'GREMLIN'
sideEffect{
    it.get().property("fullcode",   definition.value("fullnspath"));
    it.get().property("fullnspath", definition.value("fullnspath"));

    if (definition.property("isPhp").any()) { it.get().property("isPhp", true); }
    if (definition.property("isExt").any()) { it.get().property("isExt", true); }
    if (definition.property("isStub").any()) { it.get().property("isStub", true); }
}
GREMLIN
)
             ->addEFrom('TYPEHINT', 'first')
             ->back('first');
        $this->prepareQuery();

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class CreateForeachDefault extends Complete {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        // const A = [1 => 2]; foreach(A as $v) {}
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->as('v')
             ->back('first')

             ->outIs('SOURCE')
             ->atomIsNot('Variable')
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->outIs('ARGUMENT')
             ->outIsIE('VALUE')
             ->as('string')
             ->dedup(array('v', 'string'))
             ->addEFrom('DEFAULT', 'v');
        $this->prepareQuery();

        // $a = [1 => 2]; foreach($a as $v) {}
        $this->atomIs('Foreach')
             ->outIs('VALUE')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->as('v')
             ->back('first')

             ->outIs('SOURCE')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->outIs('DEFAULT')
             ->atomIs('Arrayliteral')
             ->outIs('ARGUMENT')
             ->outIsIE('VALUE')
             ->as('string')
             ->dedup(array('v', 'string'))
             ->addEFrom('DEFAULT', 'v');
        $this->prepareQuery();

        // $a = [1 => 2]; foreach($a as $k => $v) {}
        $this->atomIs('Foreach')
             ->outIs('INDEX')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->as('v')
             ->back('first')

             ->outIs('SOURCE')
             ->atomIsNot('Variable')
             ->atomIs('Arrayliteral', self::WITH_CONSTANTS)
             ->outIs('ARGUMENT')
             ->outIs('INDEX')
             ->as('string')
             ->dedup(array('v', 'string'))
             ->addEFrom('DEFAULT', 'v');
        $this->prepareQuery();

        // $a = [1 => 2]; foreach($a as $k => $v) {}
        $this->atomIs('Foreach')
             ->outIs('INDEX')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->as('v')
             ->back('first')

             ->outIs('SOURCE')
             ->atomIs('Variable')
             ->inIs('DEFINITION')
             ->outIs('DEFAULT')
             ->outIs('ARGUMENT')
             ->outIs('INDEX')
             ->as('string')
             ->dedup(array('v', 'string'))
             ->addEFrom('DEFAULT', 'v');
        $this->prepareQuery();

        // @todo : with list $a = [1 => [2, 3]]; foreach($a as $k => [$v1, $v2]) {}
        // @todo : this is getting messup when multiple loops have the same blind variables
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

use Exakat\Analyzer\Analyzer;

abstract class Complete extends Analyzer {
    protected $storageType = self::QUERY_NO_ANALYZED;

    protected function setCount(int $count): void {
        $this->gremlin->query("g.V().hasLabel(\"Analysis\").has(\"analyzer\", \"{$this->shortAnalyzer}\").property(\"count\", $count)");
    }
}

?>
   Bud1           	                                                           t e C o m p                                                                                                                                                                                                                                                                                                                                                                                                                                           C r e a t e C o m p a c t V a r i a b l e s . p h pptbLustr   A U s e r s / f a m i l l e / D e s k t o p / a n a l y z e G 3 / l i b r a r y / E x a k a t / A n a l y z e r / C o m p l e t e /    C r e a t e C o m p a c t V a r i a b l e s . p h pptbNustr    C r e a t e C o m p a c t V a r i a b l e s . p h p                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   E  	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       DSDB                                 `                                                   @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              <?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class IsPhpStructure extends IsStubStructure {
    protected const PROPERTY = 'isPhp';
    protected const PDFF     = 'phpCore';
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

use Exakat\Analyzer\Analyzer;

class PhpExtStubPropertyMethod extends Analyzer {
    public function dependsOn(): array {
        return array('Complete/CreateDefaultValues',
                    );
    }

    public function analyze(): void {
        $rulesets = exakat('rulesets');
        $exts = $rulesets->listAllAnalyzer('Extensions');

        $properties = array();
        $methods    = array();
        foreach($exts as $ext) {
            $inifile = str_replace('Extensions\Ext', '', $ext);
            if (!file_exists($this->config->dir_root . '/data/' . $inifile . '.ini')) {
                continue;
            }
            $ini = parse_ini_file($this->config->dir_root . '/data/' . $inifile . '.ini');

            if (!empty($ini['methods'][0])) {
                foreach($ini['methods'] as $fullMethod) {
                    list($class, $method) = explode('::', $fullMethod, 2);
                    array_collect_by($methods, mb_strtolower($method), makeFullNsPath($class));
                }
            }

            if (!empty($ini['properties'][0])) {
                foreach($ini['properties'] as $fullProperty) {
                    list($class, $property) = explode('::', $fullProperty, 2);
                    array_collect_by($properties, ltrim($property, '$'), makeFullNsPath($class));
                }
            }
        }

        // $mysqli->$p with typehints
        $this->atomIs('Member')
             ->isNot('isExt', true)
             ->outIs('MEMBER')
             ->fullcodeIs(array_keys($properties))
             ->savePropertyAs('fullcode', 'property')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs('Variableobject')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->outIs('TYPEHINT')
             ->atomIs(self::STATIC_NAMES)
             ->isHash('fullnspath', $properties, 'property')
             ->back('first')
             ->property('isExt', true);
        $this->prepareQuery();

        // $mysqli->$p with local new
        $this->atomIs('Member')
             ->isNot('isExt', true)
             ->outIs('MEMBER')
             ->fullcodeIs(array_keys($properties))
             ->savePropertyAs('fullcode', 'property')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs('Variableobject')
             ->filter(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('DEFAULT')
                     ->atomIs('New')
                     ->outIs('NEW')
                     ->isHash('fullnspath', $properties, 'property')
             )
             ->back('first')
             ->property('isExt', true);
        $this->prepareQuery();

        // $mysqli->m() with typehints
        $this->atomIs('Methodcall')
             ->isNot('isExt', true)
             ->outIs('METHOD')
             ->outIs('NAME')
             ->fullcodeIs(array_keys($methods), self::CASE_INSENSITIVE)
             ->savePropertyAs('fullcode', 'method')
             ->raw('sideEffect{ method = method.toLowerCase(); }')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs('Variableobject')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->outIs('TYPEHINT')
             ->atomIs(self::STATIC_NAMES)
             ->isHash('fullnspath', $methods, 'method')
             ->back('first')
             ->property('isExt', true);
        $this->prepareQuery();

        // $mysqli->m() with local new
        $this->atomIs('Methodcall')
             ->isNot('isExt', true)
             ->outIs('METHOD')
             ->outIs('NAME')
             ->fullcodeIs(array_keys($methods), self::CASE_INSENSITIVE)
             ->savePropertyAs('fullcode', 'method')
             ->raw('sideEffect{ method = method.toLowerCase(); }')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs('Variableobject')
             ->filter(
                $this->side()
                     ->inIs('DEFINITION')
                     ->outIs('DEFAULT')
                     ->atomIs('New')
                     ->outIs('NEW')
                     ->isHash('fullnspath', $methods, 'method')
             )
             ->back('first')
             ->property('isExt', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

use Exakat\Analyzer\Analyzer;

class IsStubStructure extends Analyzer {
    protected const PROPERTY = 'isStub';
    protected const PDFF     = 'stubs';

    public function dependsOn(): array {
        return array('Complete/VariableTypehint',
                    );
    }

    public function analyze(): void {
        $stubs = exakat(static::PDFF);
        $stubClassConstants   = $stubs->getClassConstantList();
        $stubProperties       = $stubs->getClassPropertyList();
        $stubStaticProperties = $stubs->getClassStaticPropertyList();
        $stubMethods          = $stubs->getClassMethodList();
        $stubStaticMethods    = $stubs->getClassStaticMethodList();

        // Adding FNP for static extended/implemented structures
        $this->atomIs('Staticconstant')
             ->isNot('isStub', true)
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->hasNoIn('DEFINITION')

             ->outIs('CONSTANT')
             ->savePropertyAs('fullcode', 'name')
             ->back('first')

             ->outIs('CLASS')
             ->inIs('DEFINITION')
             ->goToAllParents(self::INCLUDE_SELF)

             ->outIs(array('EXTENDS', 'IMPLEMENTS'))
             ->is(static::PROPERTY, true)
             ->savePropertyAs('fullnspath', 'fnp')
             ->raw('filter{ fnp + "::" + name in ***; }', $stubClassConstants)

             ->back('first')
             ->property(static::PROPERTY, true)
             ->raw('sideEffect{ it.get().property("fullnspath", fnp + "::" + name); }');
        $this->prepareQuery();

        // Adding FNP for typehinted parameter/properties
        $this->atomIs('Staticconstant')
             ->isNot('isStub', true)
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->hasNoIn('DEFINITION')

             ->outIs('CONSTANT')
             ->savePropertyAs('fullcode', 'name')
             ->back('first')

             ->outIs('CLASS')
             ->atomIs(array('Variable', 'Member', 'Staticproperty', 'Methodcall', 'Functioncall', 'Staticmethodcall')) // @todo remote or not?
             ->goToTypehint() // @todo :  also covers returntypehint ?
             ->is(static::PROPERTY, true)
             ->savePropertyAs('fullnspath', 'fnp')
             ->raw('filter{ fnp + "::" + name in ***; }', $stubClassConstants)

             ->back('first')
             ->property(static::PROPERTY, true)
             ->raw('sideEffect{ it.get().property("fullnspath", fnp + "::" + name); }');
        $this->prepareQuery();

        ////////////////////////////////////////////////////////////
        //properties (normal)

        $this->atomIs('Member')
             ->isNot('isStub', true)
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->hasNoIn('DEFINITION')

             ->outIs('MEMBER')
             ->tokenIs('T_STRING')
             ->savePropertyAs('fullcode', 'name')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'Member', 'Staticproperty', 'Methodcall', 'Functioncall', 'Staticmethodcall')) // @todo remote or not?
             ->goToTypehint() // @todo :  also covers returntypehint ?

             ->savePropertyAs('fullnspath', 'fnp')
             ->raw('filter{ fnp + "::\$" + name in ***; }', $stubProperties)

             ->back('first')
             ->property(static::PROPERTY, true);
        $this->prepareQuery();

        //static properties
        $this->atomIs('Staticproperty')
             ->isNot('isStub', true)
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->hasNoIn('DEFINITION')

             ->outIs('MEMBER')
             ->tokenIs('T_VARIABLE')
             ->savePropertyAs('fullcode', 'name')
             ->back('first')

             ->outIs('CLASS')
             ->atomIs(array('Variableobject', 'Member', 'Staticproperty', 'Methodcall', 'Functioncall', 'Staticmethodcall')) // @todo remote or not?
             ->goToTypehint() // @todo :  also covers returntypehint ?
             ->savePropertyAs('fullnspath', 'fnp')
             ->raw('filter{ fnp + "::\$" + name in ***; }', $stubStaticProperties)

             ->back('first')
             ->property(static::PROPERTY, true);
        $this->prepareQuery();

        // avec le typage parameter/return/property
        // methods
        $this->atomIs('Methodcall')
             ->isNot('isStub', true)
             ->isNot('isPhp', true)
             ->isNot('isExt', true)
             ->hasNoIn('DEFINITION')

             ->outIs('METHOD')
             ->outIs('NAME')
             ->tokenIs('T_STRING')
             ->savePropertyAs('fullcode', 'name')
             ->back('first')

             ->outIs('OBJECT')
             ->atomIs(array('Variableobject', 'Member', 'Staticproperty', 'Methodcall', 'Functioncall', 'Staticmethodcall')) // @todo remote or not?
             ->goToTypehint() // @todo :  also covers returntypehint ?
             ->is(static::PROPERTY, true)
             ->savePropertyAs('fullnspath', 'fnp')
             ->raw('filter{ fnp + "::" + name.toLowerCase() in ***; }', $stubMethods)

             ->back('first')
             ->property(static::PROPERTY, true);
        $this->prepareQuery();

        // methods defined in a stub trait
        // methods
        // Warning : methods may also be renamed with use exprssion.
        $this->atomIs(self::CLASSES_ALL)
             ->outIs('USE')
             ->outIs('USE')
             ->is(static::PROPERTY, true)
             ->values('fullnspath')
             ->unique();
        $list = $this->rawquery()->toArray();

        $traitsMethods = array_values(array_filter($stubMethods, function ($a) use ($list): bool {list($a, ) = explode('::', $a, 2); return in_array($a, $list);}));
        if (!empty($traitsMethods)) {
            $this->atomIs('Methodcall')
                 ->isNot('isStub', true)
                 ->isNot('isPhp', true)
                 ->isNot('isExt', true)
                 ->hasNoIn('DEFINITION')

                 ->outIs('METHOD')
                 ->outIs('NAME')
                 ->tokenIs('T_STRING')
                 ->savePropertyAs('fullcode', 'name')
                 ->back('first')

                 ->outIs('OBJECT')
                 ->atomIs(array('Variableobject', 'Member', 'Staticproperty', 'Methodcall', 'Functioncall', 'Staticmethodcall')) // @todo remote or not?
                 ->goToTypehint() // @todo :  also covers returntypehint ?
                 ->inIs('DEFINITION')
                 ->goToAllParentsTraits(self::INCLUDE_SELF)
                 ->isNot(static::PROPERTY, true)
                 ->outIs('USE')
                 ->outIs('USE')
                 ->is(static::PROPERTY, true)
                 ->savePropertyAs('fullnspath', 'fnp')
                 ->raw('filter{ fnp + "::" + name.toLowerCase() in ***; }', $traitsMethods)

                 ->back('first')
                 ->property(static::PROPERTY, true);
            $this->prepareQuery();
        }
        // @todo : methods defined in an abstract stub class

        //static methodcall
        $traitsStaticMethods = array_values(array_filter($stubStaticMethods, function ($a) use ($list): bool {list($a, ) = explode('::', $a, 2); return in_array($a, $list);}));
        if (!empty($traitsStaticMethods)) {
            $this->atomIs('Staticmethodcall')
                ->isNot('isStub', true)
                ->isNot('isPhp', true)
                ->isNot('isExt', true)
                ->hasNoIn('DEFINITION')

                ->outIs('METHOD')
                ->outIs('NAME')
                ->tokenIs('T_STRING')
                ->savePropertyAs('fullcode', 'name')
                ->back('first')

                ->outIs('CLASS')
                ->inIs('DEFINITION')
                ->goToAllParentsTraits(self::INCLUDE_SELF)
                ->isNot(static::PROPERTY, true)
                ->outIs('USE')
                ->outIs('USE')
                ->is(static::PROPERTY, true)
                ->savePropertyAs('fullnspath', 'fnp')
                ->raw('filter{ fnp + "::" + name.toLowerCase() in ***; }', $traitsStaticMethods)

                ->back('first')
                ->property(static::PROPERTY, true);
            $this->prepareQuery();
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassPropertyDefinitionWithTypehint extends Complete {
    public function analyze(): void {
        // $object->property->method()
        $this->atomIs('Propertydefinition', self::WITHOUT_CONSTANTS)
              ->as('property')
              ->outIs('DEFINITION')
              ->inIs('OBJECT')
              ->atomIs('Methodcall', self::WITHOUT_CONSTANTS)
              ->as('call')
              ->outIs('METHOD')
              ->atomIs('Methodcallname', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('lccode', 'name')
              ->back('first')
              ->inIs('PPP')
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs(array('METHOD', 'MAGICMETHOD'))
              ->outIs('NAME')
              ->samePropertyAs('lccode', 'name', self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'call');
        $this->prepareQuery();

        // $object->property->property2
        $this->atomIs('Propertydefinition', self::WITHOUT_CONSTANTS)
              ->as('property')
              ->outIs('DEFINITION')
              ->inIs('OBJECT')
              ->atomIs('Member', self::WITHOUT_CONSTANTS)
              ->as('call')
              ->outIs('MEMBER')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->inIs('PPP')
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('PPP')
              ->outIs('PPP')
              ->samePropertyAs('propertyname', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'call');
        $this->prepareQuery();

        // $object->property::constant
        $this->atomIs('Propertydefinition', self::WITHOUT_CONSTANTS)
              ->as('property')
              ->outIs('DEFINITION')
              ->inIs('CLASS')
              ->atomIs('Staticconstant', self::WITHOUT_CONSTANTS)
              ->as('call')
              ->outIs('CONSTANT')
              ->atomIs('Name', self::WITHOUT_CONSTANTS)
              ->savePropertyAs('code', 'name')
              ->back('first')
              ->inIs('PPP')
              ->outIs('TYPEHINT')
              ->inIs('DEFINITION')
              ->goToAllParents(self::INCLUDE_SELF)
              ->outIs('CONST')
              ->outIs('CONST')
              ->outIs('NAME')
              ->samePropertyAs('code', 'name', self::CASE_SENSITIVE)
              ->addETo('DEFINITION', 'call');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;


class IsExtStructure extends IsStubStructure {
    protected const PROPERTY = 'isExt';
    protected const PDFF     = 'phpExtensions';
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

use Exakat\Analyzer\Analyzer;

class ReturnTypehint extends Analyzer {
    protected $storageType = self::QUERY_NO_ANALYZED;

    public function analyze(): void {
        // specific for self
        $this->atomIs('Method')
             ->filter(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->atomIs('Void')
             )
             ->filter(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->raw('count().is(eq(1))')
             )
             ->outIs('RETURNED')
             ->atomIs('This')
             ->inIs('DEFINITION')
             ->atomIs('Class')

             ->as('theClass')
             ->back('first')
             ->raw(<<<'GREMLIN'
sideEffect(
        select("first").out("RETURNTYPE").fold().as("poubelle1").
        sideEffect( select("poubelle1").unfold().bothE().drop() )
    )
.addV('Identifier')
    .property("line",       -1 )
    .property("fullnspath", select("theClass").properties("fullnspath").value() )
    .property("fullcode",   select("theClass").out("NAME").properties("fullcode").value() )
    .as("b")
.sideEffect(
    // Link Returntype to Function
    select("first").addE("RETURNTYPE").to("b")
)
GREMLIN
);
        $this->prepareQuery();

        // specific for CIT
        $this->atomIs(self::FUNCTIONS_ALL)
             ->filter(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->atomIs('Void')
             )
             ->filter(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->raw('count().is(eq(1))')
             )
             ->outIs('RETURNED')
             ->atomIs('New')
             ->outIs('NEW')
             ->has('fullnspath')
             ->as('theClass')
             ->raw(<<<'GREMLIN'
sideEffect(
        select("first").out("RETURNTYPE").fold().as("poubelle1").
        sideEffect( select("poubelle1").unfold().bothE().drop() )
    )
.addV('Identifier')
    .property("line",       -1 )
    .property("fullnspath", select("theClass").properties("fullnspath").value() )
    .property("fullcode",   select("theClass").optional(out("NAME")).properties("fullcode").value() )
    .as("b")
.sideEffect(
    // Link Returntype to Function
    select("first").addE("RETURNTYPE").to("b")
)
GREMLIN
);
        $this->prepareQuery();

        $types = array(array(array('Integer'),
                             '\\\\int',
                             'int'),
                       array(array('String'),
                             '\\\\string',
                             'string'),
                       array(array('Boolean'),
                             '\\\\bool',
                             'bool'),
                       array(array('Float'),
                             '\\\\float',
                             'float'),
                       array(array('Null'),
                             '\\\\null',
                             'null'),
                       array(array('Arrayliteral'),
                             '\\\\array',
                             'array'),
        );

            // type with an atom
            $this->atomIs(self::FUNCTIONS_ALL)
                 ->filter(
                    $this->side()
                         ->outIs('RETURNTYPE')
                         ->atomIs('Void')
                 )
                 ->not(
                    $this->side()
                         ->outIs('RETURNED')
                         ->atomIsNot(array_merge(...array_column($types, 0)))
                 )
                 ->back('first')
                 ->raw(<<<'GREMLIN'
sideEffect(
        select("first").out("RETURNTYPE").fold().as("poubelle1")
        . sideEffect( select("poubelle1").unfold().bothE().drop() )

    )
.out('RETURNED')
.dedup().by(label)
.as("r")
.addV('Scalartypehint')
         .as("b")
         .coalesce(
            select("r").hasLabel('Null')        .select("b").property("fullnspath", '\\null')    .property("line", -1).property("fullcode", 'null'),
            select("r").hasLabel('Integer')     .select("b").property("fullnspath", '\\int')     .property("line", -1).property("fullcode", 'int'),
            select("r").hasLabel('String')      .select("b").property("fullnspath", '\\string')  .property("line", -1).property("fullcode", 'string'),
            select("r").hasLabel('Float')       .select("b").property("fullnspath", '\\float')   .property("line", -1).property("fullcode", 'float'),
            select("r").hasLabel('Boolean')     .select("b").property("fullnspath", '\\bool')    .property("line", -1).property("fullcode", 'bool'),
            select("r").hasLabel('Arrayliteral').select("b").property("fullnspath", '\\array')   .property("line", -1).property("fullcode", 'array'),
         )
.sideEffect(
    select("b").addE("RETURNTYPE").from("first")
)
.sideEffect(
    // Link Returntype to Function
    select("first").coalesce(
        select("first").out("RETURN").count().is(eq(1)).select("first").property("typehinttype", "one"),
        select("first").property("typehinttype", "or")
    )
)
GREMLIN
    );
        $this->prepareQuery();

        // type with a cast
        $this->atomIs(self::FUNCTIONS_ALL)
             ->filter(
                $this->side()
                     ->outIs('RETURNTYPE')
                     ->atomIs('Void')
             )
             ->filter(
                $this->side()
                     ->outIs('RETURNED')
                     ->atomIs('Cast')
             )
             ->back('first')
             ->raw(<<<'GREMLIN'
sideEffect(
        select("first").out("RETURNTYPE").fold().as("poubelle1")
        . sideEffect( select("poubelle1").unfold().bothE().drop() )

    )
.out('RETURNED').hasLabel('Cast')
.as("r")
.addV('Scalartypehint')
         .as("b")
         .coalesce(
            select("r").has('token', 'T_INT_CAST')    .select("b").property("fullnspath", '\\int')     .property("line", -1).property("fullcode", 'int'),
            select("r").has('token', 'T_STRING_CAST') .select("b").property("fullnspath", '\\string')  .property("line", -1).property("fullcode", 'string'),
            select("r").has('token', 'T_FLOAT_CAST')  .select("b").property("fullnspath", '\\float')   .property("line", -1).property("fullcode", 'float'),
            select("r").has('token', 'T_BOOL_CAST')   .select("b").property("fullnspath", '\\bool')    .property("line", -1).property("fullcode", 'bool'),
            select("r").has('token', 'T_ARRAY_CAST')  .select("b").property("fullnspath", '\\array')   .property("line", -1).property("fullcode", 'array'),
            select("r").has('token', 'T_OBJECT_CAST') .select("b").property("fullnspath", '\\object')   .property("line", -1).property("fullcode", 'array')
         )
.sideEffect(
    select("b").addE("RETURNTYPE").from("first")
)
.sideEffect(
    // Link Returntype to Function
    select("first").coalesce(
        select("first").out("RETURN").count().is(eq(1)).select("first").property("typehinttype", "one"),
        select("first").property("typehinttype", "or")
    )
)
GREMLIN
    );
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;


class SetCloneLink extends Complete {
    public function dependsOn(): array {
        return array('Complete/VariableTypehint',
                    );
    }

    public function analyze(): void {
        // class x { function __clone() {}}
        // clone (new x)
        // $a = new x; clone $a;
        $this->atomIs('Clone', self::WITHOUT_CONSTANTS)
              ->outIs('CLONE')
              ->atomIs('New', self::WITH_VARIABLES)
              ->outIs('NEW')
              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Classanonymous'), self::WITHOUT_CONSTANTS)
              ->outIs('MAGICMETHOD')
              ->outIs('NAME')
              ->codeIs('__clone', self::TRANSLATE, self::CASE_INSENSITIVE)
              ->inIs('NAME')
              ->addETo('DEFINITION', 'first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class SetClassAliasDefinition extends Complete {
    public function analyze(): void {
        // class_alias('A', 'B')
        $this->atomIs(self::CIT, self::WITHOUT_CONSTANTS)
              ->as('method')
              ->savePropertyAs('fullnspath', 'fnp')
              ->outIs('DEFINITION')
              ->is('rank', 0)
              ->inIs('ARGUMENT')
              ->atomIs('Classalias', self::WITHOUT_CONSTANTS)
              ->outWithRank('ARGUMENT', 1)
              ->outIs('DEFINITION')
              ->atomIs(array('Identifier', 'Nsname', 'Newcall', 'Name', 'Newcallname'), self::WITHOUT_CONSTANTS)
              ->dedup('')
              ->setProperty('fullnspath', 'fnp')
              ->addEFrom('DEFINITION', 'method');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;


class ExtendedTypehints extends Complete {
    public function dependsOn(): array {
        return array('Complete/SetParentDefinition',
                    );
    }
    public function analyze(): void {
       // returntype, contravariant (Interface => Class)
       // returntype, contravariant (Interface => Class => subclass)
       // returntype, contravariant (Interface => Subinterface => Class => subclass)
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->as('result')
             ->inIs('DEFINITION')
             ->atomIs(array('Interface', 'Class'))
             ->goToAllChildren(self::EXCLUDE_SELF)
             ->raw('not(where(__.out("DEFINITION").where(eq("result"))))')
             ->addETo('DEFINITION', 'result');
        $this->prepareQuery();

        // arguments
        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->outIs('TYPEHINT')
             ->atomIsNot('Void')
             ->as('result')
             ->inIs('DEFINITION')
             ->atomIs(array('Interface', 'Class'))
             ->goToAllChildren(self::EXCLUDE_SELF)
             ->raw('not(where(__.out("DEFINITION").where(eq("result"))))')
             ->addETo('DEFINITION', 'result');
        $this->prepareQuery();

        // properties
        $this->atomIs('Ppp')
             ->outIs('TYPEHINT')
             ->atomIsNot('Void')
             ->as('result')
             ->inIs('DEFINITION')
             ->atomIs(array('Interface', 'Class'))
             ->goToAllChildren(self::EXCLUDE_SELF)
             ->raw('not(where(__.out("DEFINITION").where(eq("result"))))')
             ->addETo('DEFINITION', 'result');
        $this->prepareQuery();

        // variables?

        // special case for static (PHP 8.0)
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Complete;

class PhpNativeReference extends Complete {
    public function analyze(): void {
        // PHP functions that are using references
        $functions = $this->methods->getFunctionsReferenceArgs();

        $references = array();
        foreach($functions as $function) {
            array_collect_by($references, makeFullNsPath($function['function']), $function['position']);
        }

        //sort($a);
        $this->atomFunctionIs(array_keys($references))
              ->savePropertyAs('fullnspath', 'fnp')
              ->outIs('ARGUMENT')
              ->isHash('rank', $references, 'fnp')
              ->atomIs(self::CONTAINERS)
              ->setProperty('isModified', true);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class EnumUsage extends Analyzer {
    protected array $enums = array();

    public function setEnums(array $enums): void {
        $this->enums = $enums;
    }

    public function analyze(): void {
        $enums =  makeFullNsPath($this->enums);

        // Typehint (return and argument), catch, instanceof, classes
        $this->atomIs(array('Identifier', 'Nsname'))
             ->has('line')
             ->hasIn(array('TYPEHINT', 'RETURNTYPE', 'EXTENDS', 'CLASS')) // NOT IMPLEMENT
             ->fullnspathIs($enums)
             ->analyzerIsNot('self');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class NamespaceUsage extends Analyzer {
    protected $namespaces = array();

    public function setNamespaces($namespaces) {
        $this->namespaces = $namespaces;
    }

    public function analyze(): void {
        $regex = '^(' . addslashes(addslashes(implode('|', $this->namespaces))) . ')';

        $this->atomIs(array('Nsname', 'Identifier'))
             ->has('line')
             ->hasNoIn(array('NAME' , 'MEMBER', 'CONSTANT', 'AS', 'CLASS'))
             ->has('fullnspath')
             ->regexIs('fullnspath', $regex);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class Type extends Analyzer {

    protected $type = null;

    public function analyze(): void {
        $this->atomIs($this->type);
        $this->prepareQuery();
    }

    public function getDump(): array {
        $query = <<<GREMLIN
g.V().hasLabel("{$this->type}")
.sideEffect{ line = it.get().value('line');
             fullcode = it.get().value('fullcode');
             file='None'; 
             theFunction = 'None'; 
             theClass='None'; 
             theNamespace='None'; 
             }
.sideEffect{ line = it.get().value('line'); }
.until( hasLabel('File') ).repeat( 
    __.in($this->linksDown)
      .sideEffect{ if (it.get().label() == 'Function') { theFunction = it.get().value('code')} }
      .sideEffect{ if (it.get().label() in ['Class']) { theClass = it.get().value('fullcode')} }
       )
.sideEffect{  file = it.get().value('fullcode');}

.map{ ['fullcode':fullcode, 'file':file, 'line':line, 'namespace':theNamespace, 'class':theClass, 'function':theFunction ];}

GREMLIN;

        return $this->gremlin->query($query)->toArray();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class ConstantDefinition extends Analyzer {
    protected $constants = array();

    public function analyze(): void {
        $fullnspath = makeFullNsPath($this->constants, \FNP_CONSTANT);

        $this->atomIs('Const')
             ->hasNoClassInterface()
             ->outIs('CONST')
             ->outIs('NAME')
             ->fullnspathIs($fullnspath, self::CASE_SENSITIVE)
             ->inIs('NAME');
        $this->prepareQuery();

        $this->atomIs('Defineconstant')
             ->outIs('NAME')
             ->atomIs('Identifier')
             ->hasNoOut('CONCAT')
             ->fullnspathIs($fullnspath, self::CASE_SENSITIVE);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class PhpFunctionUsage extends Analyzer {
    protected $functions = array();

    public function dependsOn(): array {
        return array('Functions/ConditionedFunctions',
                     'Functions/RedeclaredPhpFunction',
                    );
    }

    public function analyze(): void {
        $functions =  makeFullNsPath($this->functions);

        $this->atomFunctionIs($functions)
             ->not(
                 $this->side()
                      ->outIs('NAME')
                      ->inIs('USED') // function with a use expression
                      ->fullnspathIsNot($functions)
             )
             ->not(
                $this->side()
                     ->filter(
                        $this->side()
                             ->inIs('DEFINITION')
                             ->analyzerIs('Functions/ConditionedFunctions')
                             ->analyzerIs('Functions/RedeclaredPhpFunction')
                     )
             );
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class ClassDefinition extends Analyzer {
    protected $classes = array();

    public function analyze(): void {
        $classes = makeFullNsPath($this->classes);

        $this->atomIs('Class')
             ->fullnspathIs($classes);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class InterfaceUsage extends Analyzer {
    protected $interfaces = array();

    public function setInterfaces(array $interfaces): void {
        $this->interfaces = $interfaces;
    }

    public function analyze(): void {
        $interfaces =  makeFullNsPath($this->interfaces);

        $this->atomIs('Class')
             ->outIs('IMPLEMENTS')
             ->atomIs(self::STATIC_NAMES)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs('Interface')
             ->outIs('EXTENDS')
             ->atomIs(self::STATIC_NAMES)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->atomIs(self::STATIC_NAMES)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->outIs('TYPEHINT')
             ->has('line')
             ->atomIs(self::STATIC_NAMES)
             ->tokenIsNot(array('T_ARRAY', 'T_CALLABLE'))
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->has('line')
             ->atomIs(self::STATIC_NAMES)
             ->tokenIsNot(array('T_ARRAY', 'T_CALLABLE'))
             ->fullnspathIs($interfaces);
        $this->prepareQuery();

        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->atomIs(self::STATIC_NAMES)
             ->fullnspathIs($interfaces);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class ClassUsage extends Analyzer {
    protected $classes = array();

    public function setClasses($classes): void {
        $this->classes = $classes;
    }

    public function analyze(): void {
        $classes =  makeFullNsPath($this->classes);

        // New X();
        $this->atomIs(self::NEW_CALLS)
             ->hasNoIn('NAME')
             ->has('fullnspath')
             ->fullnspathIs($classes);
        $this->prepareQuery();

        $this->atomIs(array('Staticmethodcall', 'Staticproperty', 'Staticconstant', 'Staticclass'))
             ->outIs('CLASS')
             ->atomIs(self::CONSTANTS_ALL)
             ->fullnspathIs($classes);
        $this->prepareQuery();

        // Typehint (return and argument), catch, instanceof, classes
        $this->atomIs(array('Identifier', 'Nsname'))
             ->has('line')
             ->hasIn(array('TYPEHINT', 'RETURNTYPE', 'EXTENDS', 'CLASS')) // NOT IMPLEMENT
             ->fullnspathIs($classes)
             ->analyzerIsNot('self');
        $this->prepareQuery();

        $this->atomIs('Classalias')
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->noDelimiterIs($classes);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class WithoutTry extends Analyzer {
    protected $functions = array();
    protected $atoms = array();

    public function analyze(): void {
        if (!empty($this->functions)) {
            $this->atomFunctionIs($this->functions)
                 ->hasNoTryCatch()
                 ->back('first');
            $this->prepareQuery();
        }

        if (!empty($this->atoms)) {
            $this->atomIs($this->atoms)
                 ->hasNoTryCatch()
                 ->back('first');
            $this->prepareQuery();
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class FunctionDefinition extends Analyzer {
    protected $functions = array();

    public function analyze(): void {
        $fullnspath = makeFullNsPath($this->functions);

        $this->atomIs('Function')
             ->fullnspathIs($fullnspath)
             ->not(
                $this->side()
                     ->inIs('EXPRESSION')
                     ->inIs(array('THEN', 'ELSE'))
                     ->atomIs('Ifthen')
                     ->outIs('CONDITION')
                     ->filter(
                        $this->side()
                             ->atomInside('Functioncall')
                             ->fullnspathIs('\\function_exists')
                     )
             );
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class KeywordUsage extends Analyzer {
    protected $keyword = '';

    public function analyze(): void {
        if (empty($this->keyword)) {
            return;
        }

        // class, trait, interface
        $this->atomIs(array('Class', 'Trait', 'Interface'))
             ->outIs('NAME')
             ->codeIs($this->keyword, self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;
use Exakat\Graph\Helpers\GraphResults;

class MultipleDeclarations extends Analyzer {
    protected $atom = 'Class';

    public function analyze(): void {
        // case-insensitive constants

        $this->atomIs($this->atom)
             ->raw(<<<'GREMLIN'
groupCount("m").by("fullnspath").cap("m").next().findAll{ a,b -> b > 1}
GREMLIN
);
        $multiples = $this->rawQuery();

        if ($multiples->isType(GraphResults::EMPTY)) {
            return;
        }

        $fullcode = array_merge(...array_map('array_keys', $multiples->toArray()));
        $this->atomIs($this->atom)
             ->fullnspathIs($fullcode);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class UsedDirective extends Analyzer {
    protected $directives = array();

    public function analyze(): void {
        // Processing ini_get_all ?
        // ini_set($var ? )

        // ini_set('string'
        $this->atomFunctionIs(array('\\ini_set',
                                    '\\ini_get',
                                    '\\ini_restore',
                                    '\\ini_alter',
                                    '\\iconv_set_encoding',
                                    '\\get_cfg_var',
                                    ))
             ->outWithRank('ARGUMENT', 0)
             ->atomIs('String')
             ->noDelimiterIs($this->directives, self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();

        $functions = array();
        if (in_array('include_path', $this->directives, STRICT_COMPARISON)) {
            $functions[] = array('\\set_include_path',
                                 '\\get_include_path',
                                 '\\restore_include_path',
                                );
        }
        if (in_array('magic_quotes_gpc', $this->directives, STRICT_COMPARISON)) {
            $functions[] = array('\\magic_quotes_gpc',
                                );
        }

        if (in_array('magic_quotes_runtime', $this->directives, STRICT_COMPARISON)) {
            $functions[] = array('\\get_magic_quotes_runtime',
                                 '\\set_magic_quotes_runtime',
                                );
        }

        if (in_array('max_execution_time', $this->directives, STRICT_COMPARISON)) {
            $functions[] = array('\\set_time_limit',
                                );
        }

        if (empty($functions)) {
            return;
        }
        $functions = array_merge(...$functions);

        $this->atomFunctionIs($functions);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class PropertyUsage extends Analyzer {
    protected $properties = array();

    public function analyze(): void {
        $staticHash = array();
        $propertyHash = array();
        foreach($this->properties as $class => $properties) {
            foreach($properties as $property => $details) {
                if (!isset($details['fullname'])) {
                    continue;
                }
                array_collect_by($staticHash, $class, $details['fullname']);

                array_collect_by($propertyHash, $class, $property);
            }
        }

        // A::$property
        $this->atomIs('Staticproperty')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($staticHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->inIs('CLASS')
             ->outIs('MEMBER')
             ->isHash('fullcode', $staticHash, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // $a = new C; $a->property
        $this->atomIs('Member')
             ->analyzerIsNot('self')
             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs('LEFT')
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs('New')
             ->outIs('NEW')
             ->fullnspathIs(array_keys($propertyHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->outIs('MEMBER')
             ->isHash('fullcode', $propertyHash, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // function foo() : C; $a = foo(); $a->property
        $this->atomIs('Member')
             ->analyzerIsNot('self')
             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs('LEFT')
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs(array('Functioncall', 'Staticmethodcall', 'Methodcall'))
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')
             ->atomIsNot('Void')
             ->fullnspathIs(array_keys($propertyHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->outIs('MEMBER')
             ->isHash('fullcode', $propertyHash, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // function foo(C $a) { $a->property; }
        $this->atomIs('Member')
             ->analyzerIsNot('self')
             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->outIs('TYPEHINT')
             ->atomIsNot('Void')
             ->fullnspathIs(array_keys($propertyHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->outIs('MEMBER')
             ->isHash('fullcode', $propertyHash, 'fnp')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class FunctionDefaultValue extends Analyzer {
    protected $rank = -1; // -1 will prevent rank to be found
    protected $code = 0;  // 0 will prevent code to be found

    public function analyze(): void {
        $this->atomIs('Functioncall')
             ->codeIs($this->code)
             ->hasNoIn('METHOD')
             ->noChildWithRank('ARGUMENT', $this->rank)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class InterfaceDefinition extends Analyzer {
    protected $interfaces = array();

    public function analyze(): void {
        $interfaces =  makeFullNsPath($this->interfaces);

        $this->atomIs('Interface')
             ->fullnspathIs($interfaces);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class TraitUsage extends Analyzer {
    protected array $traits = array();

    public function setTraits($traits) {
        $this->traits = $traits;
    }

    public function analyze(): void {
        $traits =  makeFullNsPath($this->traits);

        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->atomIs(array('Identifier', 'Nsname', 'Self', 'Parent', 'Static'))
             ->fullnspathIs($traits);
        $this->prepareQuery();

        $this->atomIs('Staticproperty')
             ->outIs('CLASS')
             ->atomIs(array('Identifier', 'Nsname', 'Self', 'Parent', 'Static'))
             ->fullnspathIs($traits);
        $this->prepareQuery();

        // Staticconstant are not defined in traits

        // Instanceof doesn't use traits

// Check that... Const/function and aliases
        $this->atomIs('Usetrait')
             ->outIs('USE')
             ->outIsIE('NAME')
             ->tokenIs(self::STATICCALL_TOKEN)
             ->fullnspathIs($traits);
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class MethodcallUsage extends Analyzer {
    protected $calledMethods = array();

    public function analyze(): void {
        // Currently ignoring the object :(
        $calledMethods = array_map('strtolower', $this->calledMethods);

        $this->atomIs('Methodcall')
             ->outIs('METHOD')
             ->codeIs($calledMethods, self::TRANSLATE, self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class ClassConstantUsage extends Analyzer {
    protected array $classConstants = array();

    public function analyze(): void {
        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($this->classConstants))
             ->savePropertyAs('fullnspath', 'fnp')
             ->inIs('CLASS')
             ->outIs('CONSTANT')
             ->isHash('fullcode', $this->classConstants, 'fnp')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class IsSubclassOf extends Analyzer {
    protected $classes = array();

    public function analyze(): void {
        $classes =  makeFullNsPath($this->classes);

        $this->atomIs('Class')
             ->goToAllParents(self::INCLUDE_SELF)
             ->outIs(array('EXTENDS', 'IMPLEMENTS'))
             ->fullnspathIs($classes)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class UsesFrameworkFunction extends Analyzer {
    protected $functions    = array();

    public function analyze(): void {
        $analyzerId = null;

        if (!empty($this->functions[0])) {
            $functions    = makeFullNsPath($this->functions);

            if (!empty($functions)) {
                $functionsUsage = new FunctionUsage();
                $functionsUsage->setAnalyzer(get_class($this));
                $functionsUsage->setFunctions($functions);
                $analyzerId = $functionsUsage->init($analyzerId);
                $functionsUsage->run();

                $this->rowCount        += $functionsUsage->getRowCount();
                $this->processedCount  += $functionsUsage->getProcessedCount();
                $this->queryCount      += $functionsUsage->getQueryCount();
                $this->rawQueryCount   += $functionsUsage->getRawQueryCount();
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class UsesFramework extends Analyzer {
    protected $constants  = array();
    protected $functions  = array();
    protected $classes    = array();
    protected $interfaces = array();
    protected $traits     = array();
    protected $namespaces = array();
    protected $enums      = array();

    public function analyze(): void {
        $analyzerId = null;

        if (!empty($this->constants[0])) {
            $constants    = makeFullNsPath($this->constants, \FNP_CONSTANT);

            if (!empty($constants)) {
                $constantUsage = new ConstantUsage();
                $constantUsage->setAnalyzer(get_class($this));
                $constantUsage->setConstants($constants);
                $analyzerId = $constantUsage->init($analyzerId);
                $constantUsage->run();

                $this->rowCount        += $constantUsage->getRowCount();
                $this->processedCount  += $constantUsage->getProcessedCount();
                $this->queryCount      += $constantUsage->getQueryCount();
                $this->rawQueryCount   += $constantUsage->getRawQueryCount();
            }
        }

        if (!empty($this->functions[0])) {
            $functions    = makeFullNsPath($this->functions);

            if (!empty($functions)) {
                $functionsUsage = new FunctionUsage();
                $functionsUsage->setAnalyzer(get_class($this));
                $functionsUsage->setFunctions($functions);
                $analyzerId = $functionsUsage->init($analyzerId);
                $functionsUsage->run();

                $this->rowCount        += $functionsUsage->getRowCount();
                $this->processedCount  += $functionsUsage->getProcessedCount();
                $this->queryCount      += $functionsUsage->getQueryCount();
                $this->rawQueryCount   += $functionsUsage->getRawQueryCount();
            }
        }

        if (!empty($this->classes[0])) {
            $classes    = makeFullNsPath($this->classes);

            if (!empty($classes)) {
                $classesUsage = new ClassUsage();
                $classesUsage->setAnalyzer(get_class($this));
                $classesUsage->setClasses($classes);
                $analyzerId = $classesUsage->init($analyzerId);
                $classesUsage->run();

                $this->rowCount        += $classesUsage->getRowCount();
                $this->processedCount  += $classesUsage->getProcessedCount();
                $this->queryCount      += $classesUsage->getQueryCount();
                $this->rawQueryCount   += $classesUsage->getRawQueryCount();
            }
        }

        if (!empty($this->interfaces[0])) {
            $interfaces = makeFullNsPath($this->interfaces);

            if (!empty($interfaces)) {
                $interfacesUsage = new InterfaceUsage();
                $interfacesUsage->setAnalyzer(get_class($this));
                $interfacesUsage->setInterfaces($interfaces);
                $analyzerId = $interfacesUsage->init($analyzerId);
                $interfacesUsage->run();

                $this->rowCount        += $interfacesUsage->getRowCount();
                $this->processedCount  += $interfacesUsage->getProcessedCount();
                $this->queryCount      += $interfacesUsage->getQueryCount();
                $this->rawQueryCount   += $interfacesUsage->getRawQueryCount();
            }
        }

        if (!empty($this->traits[0])) {
            $traits     = makeFullNsPath($this->traits);

            if (!empty($traits)) {
                $traitsUsage = new TraitUsage();
                $traitsUsage->setAnalyzer(get_class($this));
                $traitsUsage->setTraits($traits);
                $analyzerId = $traitsUsage->init($analyzerId);
                $traitsUsage->run();

                $this->rowCount        += $traitsUsage->getRowCount();
                $this->processedCount  += $traitsUsage->getProcessedCount();
                $this->queryCount      += $traitsUsage->getQueryCount();
                $this->rawQueryCount   += $traitsUsage->getRawQueryCount();
            }
        }

        if (!empty($this->namespaces[0])) {
            $namespaces     = makeFullNsPath($this->namespaces);

            if (!empty($namespaces)) {
                $namespacesUsage = new NamespaceUsage();
                $namespacesUsage->setAnalyzer(get_class($this));
                $namespacesUsage->setNamespaces($namespaces);
                $analyzerId = $namespacesUsage->init($analyzerId);
                $namespacesUsage->run();

                $this->rowCount        += $namespacesUsage->getRowCount();
                $this->processedCount  += $namespacesUsage->getProcessedCount();
                $this->queryCount      += $namespacesUsage->getQueryCount();
                $this->rawQueryCount   += $namespacesUsage->getRawQueryCount();
            }
        }

        if (!empty($this->enums[0])) {
            $enums     = makeFullNsPath($this->enums);

            if (!empty($enums)) {
                $enumUsage = new EnumUsage();
                $enumUsage->setAnalyzer(get_class($this));
                $enumUsage->setEnums($enums);
                $analyzerId = $enumUsage->init($analyzerId);
                $enumUsage->run();

                $this->rowCount        += $enumUsage->getRowCount();
                $this->processedCount  += $enumUsage->getProcessedCount();
                $this->queryCount      += $enumUsage->getQueryCount();
                $this->rawQueryCount   += $enumUsage->getRawQueryCount();
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class None extends Analyzer {
    public function analyze(): void {
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class Extension extends Analyzer {
    protected $source = '';

    public function dependsOn(): array {
        return array('Classes/ClassUsage',
                     'Interfaces/InterfaceUsage',
                     'Traits/TraitUsage',
                     'Constants/ConstantUsage',
                     'Namespaces/NamespaceUsage',
                     'Php/DirectivesUsage',
                     'Complete/PropagateCalls',
                     );
    }

    public function analyze(): void {
        if (empty($this->source)) {
            return;
        }

        if (substr($this->source, -4) === '.ini') {
            $ini = (object) $this->loadIni($this->source);

            $ini->constants = array_filter($ini->constants ?? array());
            $constants = makeFullNsPath($ini->constants, \FNP_CONSTANT);

            $ini->functions = array_filter($ini->functions ?? array());
            $functions = makeFullNsPath($ini->functions);

            $ini->classes = array_filter($ini->classes ?? array());
            $classes = makeFullNsPath($ini->classes);

            $ini->interfaces = array_filter($ini->interfaces ?? array());
            $interfaces = makeFullNsPath($ini->interfaces);

            $ini->traits = array_filter($ini->traits ?? array());
            $traits = makeFullNsPath($ini->traits);

            $ini->enums = array_filter($ini->enums ?? array());
            $enums = makeFullNsPath($ini->enums);

            $ini->namespaces = array_filter($ini->namespaces ?? array());
            $namespaces = makeFullNsPath($ini->namespaces);

            $ini->directives = array_filter($ini->directives ?? array());
            $directives = makeFullNsPath($ini->directives);

            $ini->classconstants = array_filter($ini->classconstants ?? array());
            $classConstants = makeFullNsPath($ini->classconstants);

            $ini->properties = array_filter($ini->properties ?? array());
            $properties = makeFullNsPath($ini->properties);

            $ini->staticproperties = array_filter($ini->staticproperties ?? array());
            $staticProperties = makeFullNsPath($ini->staticproperties);

            $ini->methods = array_filter($ini->methods ?? array());
            $methods = makeFullNsPath($ini->methods);

            $ini->staticmethods = array_filter($ini->staticmethods ?? array());
            $staticMethods = makeFullNsPath($ini->staticmethods);

            $directives = array_filter($ini->directives ?? array());

            $enumCases        = array();

        } elseif (substr($this->source, -5) === '.json') {
            $json = $this->loadJson($this->source);

            $json->constants = array_filter($json->constants ?? array());
            $constants = makeFullNsPath($json->constants, \FNP_CONSTANT);

            $json->functions = array_filter($json->functions ?? array());
            $functions = makeFullNsPath($json->functions);

            $json->classes = array_filter($json->classes ?? array());
            $classes = makeFullNsPath($json->classes);

            $json->interfaces = array_filter($json->interfaces ?? array());
            $interfaces = makeFullNsPath($json->interfaces);

            $json->traits = array_filter($json->traits ?? array());
            $traits = makeFullNsPath($json->traits);

            $json->enums = array_filter($json->enums ?? array());
            $enums = makeFullNsPath($json->enums);

            $json->namespaces = array_filter($json->namespaces ?? array());
            $namespaces = makeFullNsPath($json->namespaces);

            $json->directives = array_filter($json->directives ?? array());
            $directives = makeFullNsPath($json->directives);

            $json->classconstants = array_filter($json->classconstants ?? array());
            $classConstants = makeFullNsPath($json->classconstants);

            $json->properties = array_filter($json->properties ?? array());
            $properties = makeFullNsPath($json->properties);

            $json->staticproperties = array_filter($json->staticproperties ?? array());
            $staticProperties = makeFullNsPath($json->staticproperties);

            $json->methods = array_filter($json->methods ?? array());
            $methods = makeFullNsPath($json->methods);

            $json->staticmethods = array_filter($json->staticmethods ?? array());
            $staticMethods = makeFullNsPath($json->staticmethods);

            $json->directives = array_filter($json->directives ?? array());
            $directives = makeFullNsPath($json->directives);

            $enumCases        = array();

        } elseif (substr($this->source, -5) === '.pdff') {
            $pdff = $this->loadPdff($this->source);

            $functions    = $pdff->getFunctionList();
            $constants    = $pdff->getConstantList();
            $classes      = $pdff->getClassList();
            $interfaces   = $pdff->getInterfaceList();
            $traits       = $pdff->getTraitList();
            $enums        = $pdff->getEnumList();
            $namespaces   = $pdff->getEnumList();
            $directives   = array();

            $classConstants   = $pdff->getClassConstantList();
            $properties       = $pdff->getClassPropertyList();
            $staticProperties = $pdff->getClassStaticPropertyList();
            $methods          = $pdff->getClassMethodList();
            $staticMethods    = $pdff->getClassStaticMethodList();
            $enumCases        = $pdff->getEnumCasesList();

        } else {
            return ;
        }

        $this->processFunctions($functions);
        $this->processClasses($classes);
        $this->processConstants($constants);
        $this->processInterfaces($interfaces);
        $this->processTraits($traits);
        $this->processEnums($enums);
        $this->processNamespaces($namespaces);
        $this->processDirectives($directives);

        $this->processClassConstants($classConstants);
        $this->processProperties($properties);
        $this->processStaticProperties($staticProperties);
        $this->processMethods($methods);
        $this->processStaticMethods($staticMethods);
        $this->processEnumCases($enumCases);
    }

    private function processConstants(array $constants): void {
        if (empty($constants)) {
            return;
        }

        $this->atomIs(self::STATIC_NAMES)
             ->analyzerIs('Constants/ConstantUsage')
             ->fullnspathIs($constants);
        $this->prepareQuery();
    }

    private function processFunctions(array $functions): void {
        if (empty($functions)) {
            return;
        }

        $this->atomFunctionIs($functions);
        $this->prepareQuery();
    }

    private function processClasses(array $classes): void {
        $usedClasses = array_values(array_intersect(self::getCalledClasses(), $classes));
        if (empty($usedClasses)) {
            return;
        }

        $this->atomIs('New')
             ->outIs('NEW')
             ->hasNoIn('DEFINITION')
             ->fullnspathIs($usedClasses);
        $this->prepareQuery();

        $this->atomIs(array('Staticconstant', 'Staticmethodcall', 'Staticproperty'))
             ->outIs('CLASS')
             ->hasNoIn('DEFINITION')
             ->fullnspathIs($usedClasses);
        $this->prepareQuery();

        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('ARGUMENT')
             ->outIs('TYPEHINT')
             ->has('line')
             ->hasNoIn('DEFINITION')
             ->fullnspathIs($usedClasses);
        $this->prepareQuery();

        $this->atomIs(self::FUNCTIONS_ALL)
             ->outIs('RETURNTYPE')
             ->has('line')
             ->fullnspathIs($usedClasses);
        $this->prepareQuery();

        $this->atomIs('Catch')
             ->outIs('CLASS')
             ->hasNoIn('DEFINITION')
             ->fullnspathIs($usedClasses);
        $this->prepareQuery();

        $this->atomIs('Instanceof')
             ->outIs('CLASS')
             ->hasNoIn('DEFINITION')
             ->fullnspathIs($usedClasses);
        $this->prepareQuery();
    }

    private function processInterfaces(array $interfaces): void {
        $usedInterfaces = array_values(array_intersect(self::getCalledinterfaces(), $interfaces));
        if (empty($usedInterfaces)) {
            return;
        }

        $this->analyzerIs('Interfaces/InterfaceUsage')
             ->fullnspathIs($usedInterfaces);
        $this->prepareQuery();
    }

    private function processEnums(array $enums): void {
        $usedEnums = array_values(array_intersect(self::getCalledEnums(), $enums));
        if (empty($usedEnums)) {
            return;
        }

        $this->analyzerIs('Enums/EnumUsage')
             ->fullnspathIs($usedEnums);
        $this->prepareQuery();
    }

    private function processTraits(array $traits): void {
        $usedTraits = array_values(array_intersect(self::getCalledTraits(), $traits));
        if (empty($usedTraits)) {
            return;
        }

        $this->analyzerIs('Traits/TraitUsage')
             ->fullnspathIs($usedTraits);
        $this->prepareQuery();
    }

    private function processNamespaces(array $namespaces): void {
        $usedNamespaces = array_values(array_intersect($this->getCalledNamespaces(), $namespaces));
        if (empty($usedNamespaces)) {
            return;
        }

        $this->analyzerIs('Namespaces/NamespaceUsage')
             ->fullnspathIs($usedNamespaces);
        $this->prepareQuery();
    }

    private function processDirectives(array $directives): void {
        $usedDirectives = array_values(array_intersect(self::getCalledDirectives(), $directives));
        if (empty($usedDirectives)) {
            return;
        }

        $this->analyzerIs('Php/DirectivesUsage')
             ->outWithRank('ARGUMENT', 0)
             ->noDelimiterIs($usedDirectives, self::CASE_SENSITIVE);
        $this->prepareQuery();
    }

    private function processClassConstants(array $classconstants): void {
        if (empty($classconstants)) {
            return;
        }

        $classesConstantsHash = array();
        foreach((array) $classconstants as $c) {
            list($class, $constant) = explode('::', $c, 2);
            array_collect_by($classesConstantsHash, makeFullNsPath($class), $constant);
        }

        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($classesConstantsHash))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')

             ->outIs('CONSTANT')
             ->isHash('fullcode', $classesConstantsHash, 'fqn', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }

    private function processProperties(array $properties): void {
        if (empty($properties)) {
            return;
        }

        // Properties, with typehints (parameters and properties)
        // TODO : Properties with returntypes, local new.

        $propertiesHash = array();
        foreach(array_filter($properties) as $p) {
            list($class, $property) = explode('::', $p, 2);
            array_collect_by($propertiesHash, makeFullNsPath($class), ltrim($property, '$'));
        }

        $this->atomIs('Member')
             ->outIs('OBJECT')
             // find Typehint for argument or property
             ->inIs('DEFINITION')
             ->inIsIE('NAME')
             ->outIs('TYPEHINT')
             ->atomIs(self::STATIC_NAMES)
             ->fullnspathIs(array_keys($propertiesHash))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')

             ->outIs('MEMBER')
             ->isHash('fullcode', $propertiesHash, 'fqn', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }

    private function processStaticProperties(array $staticproperties): void {
        if (empty($staticproperties)) {
            return;
        }

        // Properties, with typehints (parameters and properties)
        // TODO : Properties with returntypes, local new.
        $propertiesHash = array();
        foreach(array_filter($staticproperties) as $p) {
            list($class, $property) = explode('::', $p, 2);
            array_collect_by($propertiesHash, makeFullNsPath($class), ltrim($property, '$'));
        }

        $this->atomIs('Staticproperty')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($propertiesHash))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')

             ->outIs('MEMBER')
             ->isHash('fullcode', $propertiesHash, 'fqn', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }

    private function processMethods(array $methods): void {
        if (empty($methods)) {
            return;
        }

        // Methods, with typehints (parameters and properties)
        // TODO : Methods with returntypes, local new.
        $methodsHash = array();
        foreach(array_filter($methods) as $m) {
            list($class, $method) = explode('::', $m, 2);
            array_collect_by($methodsHash, makeFullNsPath($class), mb_strtolower($method));
        }

        $this->atomIs('Methodcall')
             ->outIs('OBJECT')
             // find Typehint for argument or property
             ->inIs('DEFINITION')
             ->inIsIE('NAME')
             ->outIs('TYPEHINT')
             ->fullnspathIs(array_keys($methodsHash))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')

             ->outIs('METHOD')
             ->outIs('NAME')
             ->tokenIs('T_STRING')
             ->isHash('fullcode', $methodsHash, 'fqn', self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }

    private function processStaticMethods(array $staticmethods): void {
        if (empty($staticmethods)) {
            return;
        }

        $staticmethodsHash = array();
        foreach(array_filter($staticmethods) as $m) {
            list($class, $method) = explode('::', $m, 2);
            array_collect_by($staticmethodsHash, makeFullNsPath($class), mb_strtolower($method));
        }

        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($staticmethodsHash))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')

             ->outIs('METHOD')
             ->outIs('NAME')
             ->tokenIs('T_STRING')
             ->isHash('fullcode', $staticmethodsHash, 'fqn', self::CASE_INSENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }

    private function processEnumCases(array $enumcases): void {
        if (empty($enumcases)) {
            return;
        }

        $enumCasesHash = array();
        foreach((array) $enumcases as $c) {
            list($class, $constant) = explode('::', $c, 2);
            array_collect_by($enumCasesHash, makeFullNsPath($class), $constant);
        }

        $this->atomIs('Staticconstant')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($enumCasesHash))
             ->savePropertyAs('fullnspath', 'fqn')
             ->back('first')

             ->outIs('CONSTANT')
             ->isHash('fullcode', $enumCasesHash, 'fqn', self::CASE_SENSITIVE)
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class ConstantUsage extends Analyzer {
    protected array $constants = array();

    public function analyze(): void {
        $constants =  makeFullNsPath($this->constants, \FNP_CONSTANT);

        $this->atomIs(array('Identifier', 'Nsname'))
             ->fullnspathIs($constants, self::CASE_SENSITIVE);
        $this->prepareQuery();
    }

    public function setConstants(array $constants): void {
        $this->constants = $constants;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class UsesFrameworkConstant extends Analyzer {
    protected $constants   = array();

    public function analyze(): void {
        $analyzerId = null;

        if (!empty($this->constants[0])) {
            $constants = makeFullNsPath($this->constants, \FNP_CONSTANT);

            if (!empty($constants)) {
                $constantsUsage = new ConstantUsage();
                $constantsUsage->setAnalyzer(get_class($this));
                $constantsUsage->setConstants($constants);
                $analyzerId = $constantsUsage->init($analyzerId);
                $constantsUsage->run();

                $this->rowCount        += $constantsUsage->getRowCount();
                $this->processedCount  += $constantsUsage->getProcessedCount();
                $this->queryCount      += $constantsUsage->getQueryCount();
                $this->rawQueryCount   += $constantsUsage->getRawQueryCount();
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;

class FunctionUsage extends Analyzer {
    protected $functions = array();

    public function analyze(): void {
        $functions =  makeFullNsPath($this->functions);

        $this->atomFunctionIs($functions);
        $this->prepareQuery();
    }

    public function setFunctions(array $functions) {
        $this->functions = $functions;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Analyzer\Common;

use Exakat\Analyzer\Analyzer;
use Exakat\Data\Dictionary;

class MethodUsage extends Analyzer {
    protected $methodList = array();

    public function analyze(): void {
        $staticHash = array();
        $methodHash = array();
        foreach($this->methodList as $class => $methods) {
            foreach($methods as $details) {
                if (isset($staticHash[$class])) {
                    $staticHash[$class][] = $details->normal_name;
                } else {
                    $staticHash[$class] = array($details->normal_name);
                }

                if (isset($methodHash[$class])) {
                    $methodHash[$class][] = $details->normal_name;
                } else {
                    $methodHash[$class] = array($details->normal_name);
                }
            }
        }

        foreach($staticHash as &$methods) {
            $methods = $this->dictCode->translate(array_unique($methods), Dictionary::CASE_INSENSITIVE);
        }
        unset($methods);
        foreach($methodHash as &$methods) {
            $methods = $this->dictCode->translate(array_unique($methods), Dictionary::CASE_INSENSITIVE);
        }
        unset($methods);

        // A::method()
        $this->atomIs('Staticmethodcall')
             ->outIs('CLASS')
             ->fullnspathIs(array_keys($staticHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->inIs('CLASS')
             ->outIs('METHOD')
             ->outIs('NAME')
             ->isHash('lccode', $staticHash, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // $a = new C; $a->method()
        $this->atomIs('Methodcall')
             ->analyzerIsNot('self')
             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs('LEFT')
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs('New')
             ->outIs('NEW')
             ->fullnspathIs(array_keys($methodHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->outIs('METHOD')
             ->isHash('lccode', $methodHash, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // function foo() : C; $a = foo(); $a->method()
        $this->atomIs('Methodcall')
             ->analyzerIsNot('self')
             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->outIs('DEFINITION')
             ->inIs('LEFT')
             ->atomIs('Assignation')
             ->outIs('RIGHT')
             ->atomIs(array('Functioncall', 'Staticmethodcall', 'Methodcall'))
             ->inIs('DEFINITION')
             ->outIs('RETURNTYPE')
             ->fullnspathIs(array_keys($methodHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->outIs('METHOD')
             ->isHash('lccode', $methodHash, 'fnp')
             ->back('first');
        $this->prepareQuery();

        // function foo(C $a) { $a->method(); }
        $this->atomIs('Methodcall')
             ->analyzerIsNot('self')
             ->outIs('OBJECT')
             ->inIs('DEFINITION')
             ->inIs('NAME')
             ->outIs('TYPEHINT')
             ->fullnspathIs(array_keys($methodHash))
             ->savePropertyAs('fullnspath', 'fnp')
             ->back('first')
             ->outIs('METHOD')
             ->isHash('lccode', $methodHash, 'fnp')
             ->back('first');
        $this->prepareQuery();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Config as Configuration;
use Exakat\Project;

class DefaultConfig extends Config {
    protected $config  = array( // directives with boolean value
                               'verbose'        => false,
                               'quick'          => false,
                               'quiet'          => false,
                               'help'           => false,
                               'recursive'      => false,
                               'update'         => false,
                               'delete'         => false,
                               'lint'           => false,
                               'json'           => false,
                               'array'          => false,
                               'dot'            => false,
                               'noDependencies' => false,
                               'noRefresh'      => false,
                               'today'          => false,
                               'none'           => false,
                               'table'          => false,
                               'text'           => false,
                               'output'         => false,
                               'debug'          => false,
                               'inplace'        => false,

                               'collect'        => false,

                               'git'            => true,
                               'svn'            => false,
                               'bzr'            => false,
                               'hg'             => false,
                               'composer'       => false,
                               'tgz'            => false,
                               'tbz'            => false,
                               'zip'            => false,
                               'rar'            => false,
                               'seven7'         => false,

                                // directives with literal value
                               'filename'           => '',
                               'dirname'            => '',
                               'program'            => '',
                               'repository'         => false,
                               'analyzers'          => array(),
                               'report'             => 'Diplomat',
                               'file'               =>  '',
                               'style'              => 'ALL',

                               'gsneo4j_host'       => '127.0.0.1',
                               'gsneo4j_port'       => '7474',
                               'gsneo4j_folder'     => 'tinkergraph',

                               'tinkergraph_host'   => '127.0.0.1',
                               'tinkergraph_port'   => '7474',
                               'tinkergraph_folder' => 'tinkergraph',

                               'branch'         => '',
                               'tag'            => '',

                               'php'           => PHP_BINARY,
                               'php52'         => '',
                               'php53'         => '',
                               'php54'         => '',
                               'php55'         => '',
                               'php56'         => '',
                               'php70'         => '',
                               'php71'         => '',
                               'php72'         => '',
                               'php73'         => '',
                               'php74'         => '',
                               'php80'         => '',

                               'phpversion'    => '7.4',
                               'token_limit'   => '1000000',

                               'baseline_use'  => 'last',    // none, last, name, number
                               'baseline_set'  => 'one',   // none, one, always

                               'concurencyCheck' => 7610,

                               'command'       => 'version',

                               'include_dirs'        => array('',
                                                             ),
                               'ignore_dirs'         => array('/assets',
                                                              '/cache',
                                                              '/css',
                                                              '/data',
                                                              '/doc',
                                                              '/docker',
                                                              '/docs',
                                                              '/example',
                                                              '/examples',
                                                              '/images',
                                                              '/js',
                                                              '/lang',
                                                              '/spec',
                                                              '/sql',
                                                              '/test',
                                                              '/tests',
                                                              '/tmp',
                                                              '/version',
                                                              '/var',
                                                             ),
                               'file_extensions'     => array('php', 'php3', 'inc', 'tpl', 'phtml', 'tmpl', 'phps', 'ctp', 'module'),
                               'project_name'        => '',
                               'project_url'         => '',
                               'project_vcs'         => 'git',
                               'project_description' => '',
                               'project_packagist'   => '',
                               'other_php_versions'  => array(),

                               'ignore_rules'        => array(),

                               'remote'              => 'none',

                               'project_reports'     => array('Diplomat',
                                                             ),
                               'project_rulesets'    => array(),

                                'inside_code'          => Configuration::WITH_PROJECTS,

                                'php_extensions'       => array('all'),
                              );

    public function __construct() {
        $this->config['project'] = new Project();
    }

    public function loadConfig(Project $project): ?string {
        return 'default';
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Project;

class DatastoreConfig extends Config {
    private $datastore             = null;
    protected $ignore_dirs         = array();
    protected $include_dirs        = array();
    protected $ignore_rules        = array();
    protected $project_name        = '';
    protected $project_url         = '';
    protected $project_vcs         = '';
    protected $project_description = '';
    protected $project_branch      = '';
    protected $project_tag         = '';
    protected Project $project;
    protected $file_extensions     = array();
    protected $stubs               = array();

    protected $options = array('phpversion'          => '',
                               'project_name'        => '',
                               'project_url'         => '',
                               'project_vcs'         => '',
                               'project_description' => '',
                               'project_branch'      => '',
                               'project_tag'         => '',
                               'file_extensions'     => array(),
                               );

    public function __construct() {
        $this->datastore = exakat('datastore');
    }

    public function setProject(Project $project): void {
        $this->project = $project;
    }

    public function loadConfig(Project $project): ?string {
        $this->options['phpversion'] = $this->datastore->getHash('php_version');
        $this->ignore_dirs           = json_decode($this->datastore->getHash('ignore_dirs')     ?? '[]');
        $this->include_dirs          = json_decode($this->datastore->getHash('include_dirs')    ?? '[]');
        $this->file_extensions       = json_decode($this->datastore->getHash('file_extensions') ?? '[]');
        $this->stubs                 = json_decode($this->datastore->getHash('stubs_config')    ?? '[]');

        $this->project_name        = $this->datastore->getHash('project');
        $this->project_url         = $this->datastore->getHash('vcs_url');
        $this->project_vcs         = $this->datastore->getHash('vcs_type');
        $this->project_description = $this->datastore->getHash('project_description');
        $this->project_branch      = $this->datastore->getHash('vcs_branch');
        $this->project_tag         = $this->datastore->getHash('vcs_tag');

        return 'datastore';
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Vcs\Vcs;
use Exakat\Tasks\Baseline;
use Exakat\Tasks\Extension;
use Exakat\Project;
use Exakat\Graph\Graph;

class CommandLine extends Config {
    private $booleanOptions = array(
                                 '-v'         => 'verbose',
                                 '-Q'         => 'quick',
                                 '-q'         => 'quiet',
                                 '-h'         => 'help',
                                 '-r'         => 'recursive',
                                 '-u'         => 'update',
                                 '-D'         => 'delete',
                                 '-l'         => 'lint',
                                 '-json'      => 'json',
                                 '-yaml'      => 'yaml',
                                 '-array'     => 'array',
                                 '-dot'       => 'dot',

                                 '-debug'     => 'debug',

                                 '--inplace'  => 'inplace',

                                 '-nodep'      => 'noDependencies',
                                 '--nodep'     => 'noDependencies',
                                 '-norefresh'  => 'noRefresh',
                                 '--norefresh' => 'noRefresh',
                                 '-text'       => 'text',
                                 '--text'      => 'text',
                                 '-o'          => 'output',
                                 '-collect'    => 'collect',
                                 '-load-dump'  => 'load_dump', // for Dump
                                 '--load-dump' => 'load_dump', // for Dump

                                 '-stop'       => 'stop',
                                 '-ping'       => 'ping',
                                 '-restart'    => 'restart',
                                 '-start'      => 'start',

    // Vcs
                                 '-svn'       => 'svn',
                                 '-bzr'       => 'bzr',
                                 '-hg'        => 'hg',
                                 '-composer'  => 'composer',
                                 '-copy'      => 'copy',    // Copy the local dir
                                 '-symlink'   => 'symlink', // make a symlink
                                 '-tgz'       => 'tgz',
                                 '-tbz'       => 'tbz',
                                 '-zip'       => 'zip',
                                 '-rar'       => 'rar',
                                 '-7z'        => 'sevenz',
                                 '-git'       => 'git',
                                 '-cvs'       => 'cvs',
                                 '-none'      => 'none',
                                 );

    private $valueOptions   = array('-f'             => 'filename',
                                    '-d'             => 'dirname',
                                    '-p'             => 'project',
                                    '-P'             => 'program',
                                    '-R'             => 'repository',
                                    '-T'             => 'project_rulesets',
                                    '-report'        => 'report',
                                    '--report'       => 'report',
                                    '-format'        => 'format',
                                    '--format'       => 'format',
                                    '-file'          => 'file',
                                    '--file'         => 'file',
                                    '-style'         => 'style',
                                    '--style'        => 'style',
                                    '-token_limit'   => 'token_limit',
                                    '--token_limit'  => 'token_limit',
                                    '-branch'        => 'branch',
                                    '--branch'       => 'branch',
                                    '-tag'           => 'tag',
                                    '--tag'          => 'tag',
                                    '-remote'        => 'remote',
                                    '--remote'       => 'remote',
                                    '-graphdb'       => 'gremlin',
                                    '--graphdb'      => 'gremlin',
                                    '-version'       => 'version',
                                    '--version'      => 'version',

                                    '-baseline-set'  => 'baseline_set',
                                    '--baseline-set' => 'baseline_set',
                                    '-baseline-use'  => 'baseline_use',
                                    '--baseline-use' => 'baseline_use',

                                    '--rules-version-max' => 'rules_version_max',
                                    '--rules-version-min' => 'rules_version_min',

                                    '--inplace'     => 'inplace',

                                    // This one is finally an array
                                    '-c'            => 'configuration',

                                    '-C'            => 'directives',
                                 );

    public static $commands = array('analyze'       => 1,
                                    'anonymize'     => 1,
                                    'constantes'    => 1,
                                    'clean'         => 1,
                                    'cleandb'       => 1,
                                    'cobble'        => 1,
                                    'testcobble'    => 1,
                                    'dump'          => 1,
                                    'doctor'        => 1,
                                    'errors'        => 1,
                                    'export'        => 1,
                                    'files'         => 1,
                                    'findextlib'    => 1,
                                    'help'          => 1,
                                    'init'          => 1,
                                    'catalog'       => 1,
                                    'remove'        => 1,
                                    'load'          => 1,
                                    'project'       => 1,
                                    'report'        => 1,
                                    'results'       => 1,
                                    'stat'          => 1,
                                    'status'        => 1,
                                    'version'       => 1,
                                    'onefile'       => 1,
                                    'test'          => 1,
                                    'update'        => 1,
                                    'upgrade'       => 1,
                                    'config'        => 1,
                                    'show'          => 1,
                                    'baseline'      => 1,
                                    'install'       => 1,
                                    );

    private $args = array();

    public function __construct() {
        $this->config['command'] = '<no-command>';
        $this->config['project'] = new Project();  // Default to no object
    }

    public function setArgs(array $args = array()): void {
        $this->args = $args;
    }

    public function loadConfig(Project $project): ?string {
        if (empty($this->args)) {
            return self::NOT_LOADED;
        }

        $args = $this->args;
        // TODO : move this to VCS
        foreach($this->booleanOptions as $key => $config) {
            $id = array_search($key, $args);
            if ($id !== false) {
                // git is default, so it should be unset if another is set
                if (in_array($config, Vcs::SUPPORTED_VCS)) {
                    $this->config = $this->config + array_fill_keys(Vcs::SUPPORTED_VCS, false);
                }
                $this->config[$config] = true;

                unset($args[$id]);
            }
        }

        foreach($this->valueOptions as $key => $config) {
            while( ($id = array_search($key, $args)) !== false ) {
                if (!isset($args[$id + 1])) {
                    // case of a name, without a following name
                    // We just ignore it
                    unset($args[$id]);
                    continue;
                }

                if (is_string($args[$id + 1]) && isset($this->valueOptions[$args[$id + 1]])) {
                    // in case this option value is actually the next option (exakat -p -T)
                    // We just ignore it
                    unset($args[$id]);
                    continue;
                }

                // Normal case is here
                switch ($config) {
                    case 'directives' :
                        $pos = strpos($args[$id + 1], '=');
                        if ($pos === false) {
                            // -C a => Ignore
                            continue 3;
                        }
                        $name  = substr($args[$id + 1], 0, $pos);
                        $name  = trim($name, '[]');
                        $value = substr($args[$id + 1], $pos + 1);

                        if (isset($this->config['directives'][$name])) {
                            $this->config['directives'][$name][] = $value;
                        } else {
                            $this->config['directives'][$name] = array($value);
                        }
                        break;

                    case 'project' :
                        if ($this->config['project']->isDefault()) {
                            $this->config['project'] = new Project($args[$id + 1]);
                        }
                        // Multiple -p are ignored : keep the first
                        break;

                    case 'configuration' :
                        if (empty($this->config['configuration'])) {
                            $this->config['configuration'] = array();
                        }
                        if (strpos($args[$id + 1], '=') === false) {
                            $name = trim($args[$id + 1]);
                            $value = '';
                        } else {
                            list($name, $value) = explode('=', trim($args[$id + 1]));
                        }
                        if (in_array($name, array('ignore_dirs', 'include_dirs', 'file_extensions'), STRICT_COMPARISON)) {
                            if (!isset($this->config['configuration'][$name])) {
                                $this->config['configuration'][$name] = array();
                            }
                            $this->config['configuration'][$name][] = $value;
                        } else {
                            $this->config['configuration'][$name] = $value;
                        }
                        break;

                    case 'gremlin' :
                        $this->config['gremlin'] = $args[$id + 1];
                        if (!in_array($this->config['gremlin'], Graph::GRAPHDB, LOOSE_COMPARISON)) {
                            $this->config['gremlin'] = 'nogremlin';
                        }
                        break;

                    case 'format' :
                        if (isset($this->config['project_reports'])) {
                            $this->config['project_reports'][] = $args[$id + 1];
                        } else {
                            $this->config['project_reports'] = array($args[$id + 1]);
                        }
                        break;

                    case 'filename' :
                        if (isset($this->config['filename'])) {
                            $this->config['filename'][] = $args[$id + 1];
                        } else {
                            $this->config['filename'] = array($args[$id + 1]);
                        }
                        $this->config['filename'] = array_unique($this->config['filename']);
                        break;

                    case 'program' :
                        if (isset($this->config['project_rulesets'])) {
                            // program and project_rulesets are mutually exclusive
                            break;
                        } elseif (!isset($this->config['program'])) {
                            $this->config['program'] = array($args[$id + 1]);
                        } else {
                            $this->config['program'][] = $args[$id + 1];
                        }
                        break;

                    case 'project_rulesets' :
                        if (isset($this->config['program'])) {
                            // program and project_rulesets are mutually exclusive
                            break;
                        } elseif (isset($this->config['project_rulesets'])) {
                            $this->config['project_rulesets'][] = $args[$id + 1];
                        } else {
                            $this->config['project_rulesets'] = array($args[$id + 1]);
                        }
                        break;

                    default:
                        $this->config[$config] = $args[$id + 1];
                }

                unset($args[$id]);
                unset($args[$id + 1]);
            }
        }

        $command = array_shift($args);
        if (isset($command, self::$commands[$command])) {
            $this->config['command'] = $command;

            if ($this->config['command'] === 'extension') {
                $subcommand = array_shift($args);
                if (!in_array($subcommand, Extension::ACTIONS, STRICT_COMPARISON)) {
                    $subcommand = 'local';
                }
                $this->config['subcommand'] = $subcommand;

                if (in_array($subcommand, array('install', 'uninstall', 'update'), STRICT_COMPARISON)) {
                    $this->config['extension'] = array_shift($args);
                }
            } elseif ($this->config['command'] === 'baseline') {
                $subcommand = array_shift($args);
                if (!in_array($subcommand, Baseline::ACTIONS, STRICT_COMPARISON)) {
                    $subcommand = 'list';
                }
                $this->config['subcommand'] = $subcommand;

                if (in_array($subcommand, array('remove'), STRICT_COMPARISON)) {
                    $this->config['baseline_id'] = array_shift($args);
                } elseif (in_array($subcommand, array('save'), STRICT_COMPARISON)) {
                    $this->config['baseline_set'] = array_shift($args);
                }
            }
        } else {
            $this->config['command']       = 'unknown';
            $this->config['command_value'] = $command ?? '<no-command>';
        }

        if (!empty($args)) {
            $c = count($args);
            if (isset($this->config['verbose'])) {
                display( 'Found ' . $c . ' argument' . ($c > 1 ? 's' : '') . ' that ' . ($c > 1 ? 'are' : 'is') . " not understood.\n\n\"" . implode('", "', $args) . "\"\n\nIgnoring " . ($c > 1 ? 'them all' : 'it' . ".\n"));
            }
        }

        // Special case for onepage command. It will only work on 'onepage' project
        if ($this->config['command'] == 'onepage') {

            $this->config['project']   = 'onepage';
            $this->config['ruleset']   = 'OneFile';

            $this->config['format']    = array('OnepageJson');
            $this->config['file']      = str_replace('/code/', '/reports/', substr($this->config['filename'], 0, -4));
            $this->config['quiet']     = true;
            $this->config['norefresh'] = true;
        }

        return 'commandline';
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Project as Project;

class EmptyConfig extends Config {

    public function loadConfig(Project $project): ?string {
        return null;
    }

    public function get(string $index) {
        if ($index === 'project') {
            return new Project();
        }

        return $this->config[$index] ?? null;
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Phpexec;
use Exakat\Project;
use Exakat\Config as Configuration;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Yaml\Exception\ParseError;

class DotExakatYamlConfig extends Config {
    public const YAML_FILE = '.exakat.yml';
    private $dotExakatYaml = '';
    private $rulesets = array();

    public function __construct(Project $project, string $projects_root) {
        if ($project->isDefault()) {
            $this->config['inside_code'] = Configuration::INSIDE_CODE;
            $this->dotExakatYaml = getcwd() . '/' . self::YAML_FILE;
        } else {
            $this->config['inside_code'] = Configuration::WITH_PROJECTS;
            $this->dotExakatYaml = $projects_root . '/projects/' . (string) $project . '/code/' . self::YAML_FILE;
        }

        if (!file_exists($this->dotExakatYaml)) {
            $secondary = substr($this->dotExakatYaml, 0, -3) . 'yaml';
            if (file_exists($secondary)) {
                $this->dotExakatYaml = $secondary;
            } else {
                $this->dotExakatYaml = '';
            }
        }

        $this->config['project'] = $project;
    }

    public function loadConfig(Project $project): ?string {
        if (!file_exists($this->dotExakatYaml)) {
            $this->config['project']     = new Project();

            return self::NOT_LOADED;
        }

        try {
            $tmp_config = Yaml::parseFile($this->dotExakatYaml);
        } catch (ParseError $exception) {
            print 'Error while parsing ' . basename($this->dotExakatYaml) . '. Configuration ignored.' . PHP_EOL;

            return self::NOT_LOADED;
        }

        if (!is_array($tmp_config)) {
            // Can't use display while in config phase
            display("Failed to parse YAML file. Please, check its syntax.\n");
            return self::NOT_LOADED;
        }

        // removing empty values in the INI file
        foreach($tmp_config as &$value) {
            if (is_array($value) && empty($value[0])) {
                unset($value[0]);
            }
        }
        unset($value);

        $other_php_versions = array();
        foreach(Configuration::PHP_VERSIONS as $version) {
            $phpVersion = "php$version";
            if (empty($this->config->{$phpVersion})) {
                continue;
            }
            $php = new Phpexec($version[0] . '.' . $version[1], $this->config->{$phpVersion});
            if ($php->isValid()) {
                $other_php_versions[] = $version;
            }
        }

        // check and default values
        $defaults = array( 'other_php_versions' => $other_php_versions,
                           'file_extensions'    => array('php', 'php3', 'inc', 'tpl', 'phtml', 'tmpl', 'phps', 'ctp', 'module'),
                           'project_rulesets'   => 'CompatibilityPHP53,CompatibilityPHP54,CompatibilityPHP55,CompatibilityPHP56,CompatibilityPHP70,CompatibilityPHP71,CompatibilityPHP72,CompatibilityPHP73,CompatibilityPHP74,Dead code,Security,Analyze,Preferences,Appinfo,Appcontent',
                           'project_reports'    => array('Text'),
                           'ignore_dirs'        => array('/assets',
                                                         '/cache',
                                                         '/css',
                                                         '/data',
                                                         '/doc',
                                                         '/docker',
                                                         '/docs',
                                                         '/example',
                                                         '/examples',
                                                         '/images',
                                                         '/js',
                                                         '/lang',
                                                         '/spec',
                                                         '/sql',
                                                         '/test',
                                                         '/tests',
                                                         '/tmp',
                                                         '/version',
                                                         '/var',
                                                        ),
                           'include_dirs'        => array(),
                           'rulesets'            => array(),
                           'project'             => null,
                           'project_name'        => '',
                           'project_url'         => '',
                           'project_vcs'         => '',
                           'project_description' => '',
                           'project_branch'      => '',
                           'project_tag'         => '',

                           'stubs'               => array(),

                           'ignore_rules'        => array(),
                        );

        foreach(array_keys($defaults) as $name) {
            if (!empty($tmp_config[$name])) {
                $this->config[$name] = $tmp_config[$name];
            }
            unset($tmp_config[$name]);
        }

        if (!empty($tmp_config)) {
            display(count($tmp_config) . ' entries were found in .exakat.yaml, but could not be processed : ' . implode(', ', array_keys($tmp_config)));
        }

        if (isset($this->config['project_themes'])) {
            if (isset($tmp_config['project_themes'])) {
                display("please, rename project_themes into project_rulesets in your .exakat.yaml file\n");

                if (empty($this->config['project_rulesets'])) {
                    $this->config['project_rulesets'] = $this->config['project_themes'];
                }
            }
        }

        if (isset($this->config['other_php_versions'])) {
            if (is_string($this->config['other_php_versions'])) {
                $this->config['other_php_versions'] = listToArray($this->config['other_php_versions']);
                foreach($this->config['other_php_versions'] as &$version) {
                    $version = str_replace('.', '', trim($version));
                }
                unset($version);
            }
        }

        if (isset($this->config['file_extensions'])) {
            if (is_string($this->config['file_extensions'])) {
                $this->config['file_extensions'] = listToArray($this->config['file_extensions']);
            }
            $this->config['file_extensions'] = $this->cleanFileExtensions($this->config['file_extensions']);
        }


        if (isset($this->config['project_reports'])) {
            if (is_string($this->config['project_reports'])) {
                $this->config['project_reports'] = listToArray($this->config['project_reports']);
                foreach($this->config['project_reports'] as &$ext) {
                    $ext = trim($ext);
                }
                unset($ext);
            }
        }

        if (isset($this->config['project_rulesets'])) {
            if (is_string($this->config['project_rulesets'])) {
                $this->config['project_rulesets'] = listToArray($this->config['project_rulesets']);
                foreach($this->config['project_rulesets'] as &$ext) {
                    $ext = trim($ext);
                }
                unset($ext);
            }
        }

        if (isset($this->config['project_name'])) {
            $this->config['project'] = new Project(mb_strtolower(preg_replace('/\W/', '_', $this->config['project_name'] )));
        } else {
            $this->config['project'] = new Project('in-code-audit');
        }
        if (isset($this->config['rulesets'])) {
            // clean the read
            $this->rulesets = RulesetConfig::cleanRulesets($this->config['rulesets']);

            unset($this->config['rulesets']);
        }

        foreach($tmp_config as $name => $tmp) {
            if (class_exists('Exakat\\Analyzer\\' . str_replace('/', '\\', $name))) {
                $this->config[$name] = $tmp;
                unset($tmp_config[$name]);
            }
        }

        $this->config['phpversion'] = substr(PHP_VERSION, 0, 3);

        if (!empty($tmp_config)) {
            display('Ignoring ' . count($tmp_config) . ' unkown directives : ' . implode(', ', array_keys($tmp_config)));
        }

        // Collect stubs. Stubs MUST be in the same code repository, so they are chrooted with the current directory.
//        $stubs = array();
//        $code_dir = getcwd();
        /*
        $this->config['stubs'] = makeArray($this->config['stubs']);
        foreach($this->config['stubs'] as $stub) {
            $d = getcwd();
            $path = realpath($code_dir . $stub);
            if ($path === false) {
                continue;
            }

            if (!file_exists($path)) {
                $stubs[$stub] = array();

                continue;
            }

            if (is_file($path)) {
                $stubs[$stub] = array($stub);

                continue;
            }

            if (is_dir($path)) {
                chdir($path);
                $allFiles = rglob('.');
                $allFiles = array_map(function (string $path) use ($stub): string { return $stub . ltrim($path, '.'); }, $allFiles);
                chdir($d);

                $stubs[$stub] = $allFiles;
            }
        }
        $this->config['stubs'] = array_unique(array_merge(...array_values($stubs)));
        */

        return self::YAML_FILE;
    }

    public function getRulesets(): array {
        return $this->rulesets;
    }

    public function getConfig(string $dir_root = ''): string {
        $defaultConfig = new DefaultConfig();

        // $vendor
        if (!isset($this->config['include_dirs'])) {
            $include_dirs = 'include_dirs[] = "";';
        } elseif ($this->config['include_dirs'] === array('/')) {
            $include_dirs = 'include_dirs[] = "";';
        } else {
            $include_dirs = 'include_dirs[] = "' . implode("\";\ninclude_dirs[] = \"", $this->config['include_dirs']) . "\";\n";
        }

        if (isset($this->config['file_extensions'])) {
            $file_extensions  = implode(',', $this->config['file_extensions'] ?? array());
        } else {
            $file_extensions  = $defaultConfig->get('file_extensions');
        }

        $default = array();
        $iniFiles = glob("$dir_root/human/en/*/*.ini");
        foreach($iniFiles as $file) {
            $ini = parse_ini_file($file, \INI_PROCESS_SECTIONS);
            if (isset($ini['parameter1'])) {
                $default[basename(dirname($file)) . '/' . basename($file, '.ini')][$ini['parameter1']['name']] = $ini['parameter1']['default'] ?? '';
            }
        }

        foreach($this->config as $key => $value) {
            if (strpos($key, '/') === false) {
                continue;
            }

            foreach($value as $name => $values) {
                if (isset($default[$key])) {
                    $default[$key][$name] = $values;
                }
            }
        }

        $configIni = array(
'project'             => (string) $this->config['project'],
'project_name'        => (string) $this->config['project_name'],
'project_url'         => (string) ($this->config['project_url']         ?? ''),
'project_vcs'         => (string) ($this->config['project_vcs']         ?? ''),
'project_description' => (string) ($this->config['project_description'] ?? ''),
'project_branch'      => (string) ($this->config['project_branch']      ?? ''),
'project_tag'         => (string) ($this->config['project_tag']         ?? ''),
'include_dirs'        => $this->config['include_dirs'] ?? $defaultConfig->get('include_dirs'),
'ignore_dirs'         => $this->config['ignore_dirs']  ?? $defaultConfig->get('ignore_dirs'),
'ignore_rules'        => $this->config['ignore_rules'] ?? $defaultConfig->get('ignore_rules'),
'file_extensions'     => $file_extensions,
'custom'              => $default,
        );

        return Yaml::dump($configIni);
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Project as Project;

class RulesetConfig extends Config {
    private $remoteIniFile = false;

    public function __construct(string $exakat_root) {
        // Normal case : rulesets.ini
        $this->remoteIniFile = "{$exakat_root}/config/rulesets.ini";
        if (file_exists($this->remoteIniFile)) {
            return;
        }

        // Old case : rulesets.ini
        $this->remoteIniFile = "{$exakat_root}/config/themes.ini";
        if (file_exists($this->remoteIniFile)) {
            display("Warning : config/themes.ini is obsolete, and will be replaced by config/rulesets.ini. Please, rename it.\n");

            return;
        }

        $this->remoteIniFile = false;
    }

    public function loadConfig(Project $project): ?string {
        if (empty($this->remoteIniFile)) {
            return self::NOT_LOADED;
        }

        $ini = parse_ini_file($this->remoteIniFile, \INI_PROCESS_SECTIONS);
        if (empty($ini)) {
            return self::NOT_LOADED;
        }

        foreach($ini as $name => $values) {
            if (!isset($values['analyzer'])) {
                continue;
            }

            if (!is_array($values['analyzer'])) {
                continue;
            }

            $list = array_filter(array_unique($values['analyzer']), 'filter_analyzer');

            if (empty($list)) {
                continue;
            }

            // Check for actual existence and drop unknown
            $this->config[$name] = $list;
        }

        $this->config = self::cleanRulesets($this->config);

        return 'config/rulesets.ini';
    }

    public static function cleanRulesets(array $rulesets): array {
        // hash=>array

        // This will filter out rules made of a single string
        $rulesets = array_filter($rulesets, 'is_array');
        $rulesets = array_map('array_values', $rulesets);

        $rulesets = array_map(function (array $rules): array {
            return preg_grep('#^[^/]+/[^/]+$#', $rules);
        }, $rulesets);


        $rulesets = array_filter($rulesets);
        $rulesets = array_map('array_filter', $rulesets);

        $rulesets = array_map('array_unique', $rulesets);

        return $rulesets;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Project as Project;
use Symfony\Component\Yaml\Yaml as Symfony_Yaml;

abstract class Config {
    public const NOT_LOADED = null;

    protected $config  = array();
    protected $options = array();

    protected $ignore_dirs;
    protected $include_dirs;

    protected Project $project;

    abstract public function loadConfig(Project $project): ?string ;

    public function toArray(): array {
        return $this->config;
    }

    public function get(string $index) {
        if (!isset($this->config[$index])) {
            print "No such config as $index in " . get_class(static::class) . PHP_EOL;
            return null;
        }
        return $this->config[$index];
    }

    public function toIni(): string {
        $ini = array();

        $ini[] = ';Main PHP version for this code.';
        $ini[] = "phpversion = {$this->options['phpversion']}";
        $ini[] = '';

        $ini[] = ';Ignored dirs and files, relative to code source root.';
        foreach($this->ignore_dirs as $ignore_dir) {
            $ini[] = "ignore_dirs[] = \"$ignore_dir\"";
        }
        $ini[] = '';

        $ini[] = ';Included dirs or files, relative to code source root. Default to all.';
        $ini[] = ';Those are added after ignoring directories';
        foreach($this->include_dirs as $include_dir) {
            $ini[] = "include_dirs[] = \"$include_dir\"";
        }
        $ini[] = '';

        $ini[] = ';Accepted file extensions';
        $ini[] = 'file_extensions = "' . implode(',', $this->file_extensions) . '"';
        $ini[] = '';

        $ini[] = ';Stub files and folders';
        if (empty($this->stubs)) {
            $ini[] = "stub[] = '';";
        } else {
            foreach($this->stubs as $stub) {
                $ini[] = "stub[] = \"$stub\"";
            }

        }
        $ini[] = '';

        $ini[] = ';Ignored rules';
        foreach($this->ignore_rules as $ignore_rule) {
            $ini[] = "ignore_rules[] = \"$ignore_rule\"";
        }
        $ini[] = '';

        $ini[] = ';Description of the project';
        $ini[] = "project_name        = \"{$this->project_name}\";";
        $ini[] = "project_url         = \"{$this->project_url}\";";
        $ini[] = "project_vcs         = \"{$this->project_vcs}\";";
        $ini[] = "project_description = \"{$this->project_description}\";";
        $ini[] = "project_branch      = \"{$this->project_branch}\";";
        $ini[] = "project_tag         = \"{$this->project_tag}\";";
        $ini[] = '';

        $parameters = preg_grep('#^[A-Z][^/]+/[A-Z].+$#', array_keys($this->options));
        foreach($parameters as $parameter) {
            $class = "\Exakat\Analyzer\\" . str_replace('/', '\\', $parameter);
            if (!class_exists($class)) {
                continue;
            }
            $ini[] = "[$parameter]";
            foreach($this->options[$parameter] as $name => $value) {
                if (!property_exists($class, $name)) {
                    continue;
                }
                $ini[] = "$name = $value;";
            }
            $ini[] = '';
        }

        return implode(PHP_EOL, $ini);
    }

    public function toYaml(): string {
        $yaml = array('phpversion'          => $this->options['phpversion'],
                      'ignore_dirs'         => $this->ignore_dirs,
                      'include_dirs'        => $this->include_dirs,
                      'ignore_rules'        => $this->ignore_rules,
                      'file_extensions'     => $this->file_extensions,
                      'stub'                => $this->stubs,
                      'project_name'        => $this->project_name,
                      'project_url'         => $this->project_url,
                      'project_vcs'         => $this->project_vcs,
                      'project_description' => $this->project_description,
                      'project_branch'      => $this->project_branch,
                      'project_tag'         => $this->project_tag,
                      );

        $parameters = preg_grep('#^[A-Z][^/]+/[A-Z].+$#', array_keys($this->options));
        foreach($parameters as $parameter) {
            $class = "\Exakat\Analyzer\\" . str_replace('/', '\\', $parameter);
            if (!class_exists($class)) {
                continue;
            }
            $yaml[$parameter] = array();
            foreach($this->options[$parameter] as $name => $value) {
                if (!property_exists($class, $name)) {
                    continue;
                }
                $yaml[$parameter][$name] = $value;
            }
        }

        return Symfony_Yaml::dump($yaml);
    }

    protected function cleanFileExtensions(array $extensions): array {
        $filter = function ($s) {
            if (!is_string($s)) { return ''; }
            return trim($s, '. ');
        };
        $extensions = array_map($filter, $extensions);
        $extensions = array_filter($extensions);

        return $extensions;
    }

    protected function cleanProjectReports(array $reports): array {
        $filter = function ($s) {
            if (!is_string($s)) { return ''; }
            return trim($s, '. -/');
        };
        $reports = array_map($filter, $reports);
        $reports = array_filter($reports);

        return $reports;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Phpexec;
use Exakat\Project;
use Exakat\Vcs\Vcs;

class ProjectConfig extends Config {
    private string  $projects_root = '.';
    protected Project $project;

    protected $config = array('phpversion'          => PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION,
                              'project_name'        => '',
                              'project_url'         => '',
                              'project_vcs'         => 'git',
                              'project_description' => '',
                              'project_branch'      => '',
                              'project_tag'         => '',
                              'project_rulesets'    => array(),
// No default value,
//                              'project_rulesets'    => array(),
                              'file_extensions'     => array('php',
                                                             'php3',
                                                             'inc',
                                                             'tpl',
                                                             'phtml',
                                                             'tmpl',
                                                             'phps',
                                                             'ctp',
                                                             'module',
                                                             ),
                              'include_dirs'        => array(),
                              'ignore_dirs'         => array(),
                              'ignore_rules'        => array(),
                              'stubs'               => array(),
                              );

    public function __construct(string $projects_root) {
        $this->projects_root = "$projects_root/projects/";
    }

    public function setProject(Project $project): void {
        $this->project = $project;
    }

    public function loadConfig(Project $project): ?string {
        $this->project = $project;

        $pathToIni = "{$this->projects_root}{$project}/config.ini";
        if (!file_exists($pathToIni)) {
            $this->config = array_filter($this->config);
            display("No config.ini for project named '$project'\n");

            return self::NOT_LOADED;
        }

        $ini = parse_ini_file($pathToIni, \INI_PROCESS_SECTIONS);
        if (!is_array($ini)) {
            $error = error_get_last();
            display("Couldn't parse $pathToIni : $error[message]\nIgnoring file\n");

            $this->config = array_filter($this->config);

            return self::NOT_LOADED;
        }

        foreach(array_keys($this->config) as $key) {
            if (!isset($ini[$key])) {
                $ini[$key] = $this->config[$key];
            }
        }

        // Aliasing project_themes into rulesets
        if (isset($ini['project_themes'])) {
            print "rename project_themes in project_rulesets, in your config.ini file\n";

            if (empty($this->config['project_rulesets'])) {
                $this->config['project_rulesets'] = $ini['project_themes'];
            }
        }
        $this->config = $ini;

        $pathToCache = "{$this->projects_root}{$project}/config.cache";
        if (file_exists($pathToCache)) {
            $iniCache = parse_ini_file($pathToCache);
            if (isset($iniCache['ignore_dirs'])) {
                $this->config['ignore_dirs'] = array_merge($this->config['ignore_dirs'],
                                                           $iniCache['ignore_dirs']);
            }
        }

        $this->config['project_vcs'] = $this->config['project_vcs'] ?? '';

        // Default behavior to keep exakat running until everyone has a filled file_extension option in config.ini
        if (empty($this->config['file_extensions'])) {
            $this->config['file_extensions'] = explode(',', 'php,php3,inc,tpl,phtml,tmpl,phps,ctp,module');
        } elseif (is_string($this->config['file_extensions'] )) {
            $this->config['file_extensions'] = str2array($this->config['file_extensions'] );
        }
        $this->config['file_extensions'] = $this->cleanFileExtensions($this->config['file_extensions']);

        // Converting the string format to arrays when necessary
        if (isset($this->config['other_php_versions']) &&
            is_string($this->config['other_php_versions'])) {
            $this->config['other_php_versions'] = listToArray($this->config['other_php_versions']);
            foreach($this->config['other_php_versions'] as &$version) {
                $version = trim($version, '. ');
            }
            unset($version);
        }

        if (isset($this->config['file_extensions']) &&
            is_string($this->config['file_extensions'])) {
            $this->config['file_extensions'] = listToArray($this->config['file_extensions']);
            foreach($this->config['file_extensions'] as &$ext) {
                $ext = trim($ext, '. ');
            }
            unset($ext);
        }

        if (!isset($this->config['phpversion']) ||
             $this->config['phpversion'] === 'PHP' ||
             !in_array($this->config['phpversion'], Phpexec::VERSIONS)) {
            $this->config['phpversion'] = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
        }
        // else ALL is good

        if (isset($this->config['project_reports']) &&
            is_string($this->config['project_reports'])) {
            $this->config['project_reports'] = listToArray($this->config['project_reports']);
            foreach($this->config['project_reports'] as &$ext) {
                $ext = trim($ext);
            }
            unset($ext);
        }

        if (isset($this->config['project_rulesets'])) {
            if (is_string($this->config['project_rulesets'])) {
                $this->config['project_rulesets'] = listToArray($this->config['project_rulesets']);
            }

            $this->config['project_rulesets'] = array_filter($this->config['project_rulesets'] ?? array());
            $this->config['project_rulesets'] = array_map('trim', $this->config['project_rulesets']);
            $this->config['project_rulesets'] = array_unique($this->config['project_rulesets']);
        }

        if (in_array($this->config['project_vcs'], Vcs::SUPPORTED_VCS)) {
            $this->config['git'] = false; // remove Git, which is by default
            $this->config[$this->config['project_vcs']] = true; // potentially, revert git
        }

        if (isset($this->config['rules_version_max']) &&
            !preg_match('/^(\d+)\.(\d+)\.(\d+)$/', $this->config['rules_version_max'])) {
            $this->config['rules_version_max'] = '';
        }

        if (isset($this->config['rules_version_min']) &&
            !preg_match('/^(\d+)\.(\d+)\.(\d+)$/', $this->config['rules_version_min'])) {
            $this->config['rules_version_min'] = '';
        }

        // Calculate the stubs recursivement if it is a folder
        // all path are relative to the project_dir/code, cannot be outside.
        $stubs = array(array());
        $code_dir="{$this->projects_root}{$project}/code/";
        $this->config['stubs'] = makeArray($this->config['stubs']);
        foreach($this->config['stubs'] as $stub) {
            $d = getcwd();
            $path = realpath($code_dir . $stub);

            if ($path === false) {
                continue;
            }

            if (!file_exists($path)) {
                $stubs[$stub] = array();

                continue;
            }

            if (is_file($path)) {
                $stubs[$stub] = array($stub);

                continue;
            }

            if (is_dir($path)) {
                chdir($path);
                $allFiles = rglob('.');
                $allFiles = array_map(function (string $path) use ($stub): string { return $stub . ltrim($path, '.'); }, $allFiles);
                chdir($d);

                $stubs[$stub] = $allFiles;
            }
        }
        $this->config['stubs'] = array_unique(array_merge(...array_values($stubs)));

        // remove unset values
        $this->config = array_filter($this->config);

        return "$project/config.ini";
    }

    // required for Init Project
    public function setConfig(string $name, $value): void {
        $this->config[$name] = $value;
    }

    public function getConfig(string $dir_root = ''): string {
        // $vendor
        if ($this->config['include_dirs'] === array('/')) {
            $include_dirs = 'include_dirs[] = "";';
        } else {
            $include_dirs = 'include_dirs[] = "' . implode("\";\ninclude_dirs[] = \"", $this->config['include_dirs']) . "\";\n";
        }
        $ignore_dirs   = self::makeIniArray('ignore_dirs', $this->config['ignore_dirs']);
        $ignore_rules  = self::makeIniArray('ignore_rules', $this->config['ignore_rules'] ?? array());

        $file_extensions  = implode(',', $this->config['file_extensions']);

        $custom_configs = array();

        $iniFiles = glob("$dir_root/human/en/*/*.ini");
        $default = array();
        foreach($iniFiles as $file) {
            $ini = parse_ini_file($file, \INI_PROCESS_SECTIONS);
            if (isset($ini['parameter1']['default'])) {
                $default[basename(dirname($file)) . '/' . basename($file, '.ini')][$ini['parameter1']['name']] = $ini['parameter1']['default'];
            }
        }

        foreach($this->config as $key => $value) {
            if (strpos($key, '/') === false) {
                continue;
            }

            $cc = "[$key]\n";
            foreach($value as $name => $values) {
                if (is_array($values)) {
                    $cc .= "{$name}[] = " . implode(";\n{$name}[] = ", $values) . ";\n; default = {$default[$key][$name]}\n";
                } elseif (is_string($values)) {
                    if (intval($values) === 0) {
                        $cc .= "{$name} = \"$values\";\n; default = {$default[$key][$name]}\n";
                    } else {
                        $cc .= "{$name} = $values;\n; default = {$default[$key][$name]}\n";
                    }
                } elseif (is_int($values)) {
                    $cc .= "{$name} = $values;\n; default = {$default[$key][$name]}\n";
                } else {
                    assert(false, 'Unknown type for INI creation : ' . gettype($values));
                }

                unset($default[$key]);
            }
            $cc .= PHP_EOL;

            $custom_configs[] = $cc;
        }

        foreach($default as $key => $value) {
            $cc2 = "[$key]\n";
            foreach($value as $name => $values) {
                if (is_array($values)) {
                    $cc2 .= "{$name}[] = " . implode(";\n{$name}[] = ", $values) . ";\n; default value\n\n";
                } elseif (is_string($values)) {
                    if ((int) $values === 0) {
                        $cc2 .= "{$name} = \"$values\";\n; default value\n\n";
                    } else {
                        $cc2 .= "{$name} = $values;\n; default value\n\n";
                    }
                } elseif (is_int($values)) {
                    $cc2 .= "{$name} = $values;\n; default value\n\n";
                } else {
                    assert(false, 'Unknown type for INI creation : ' . gettype($values));
                }
            }
            $custom_configs[] = $cc2;
        }

        $custom_configs = implode('', $custom_configs);

        $configIni = <<<INI
;Main PHP version for this code.
;default is to use config/exakat.ini
;phpversion = {$this->config['phpversion']}

;Ignored dirs and files, relative to code source root.
$ignore_dirs

;Ignored rules.
$ignore_rules

;Included dirs or files, relative to code source root. Default to all.
;Those are added after ignoring directories
$include_dirs

;Accepted file extensions
file_extensions = $file_extensions

;Description of the project
project_name        = "{$this->config['project_name']}";
project_url         = "{$this->config['project_url']}";
project_vcs         = "{$this->config['project_vcs']}";
project_description = "{$this->config['project_description']}";
project_branch      = "{$this->config['project_branch']}";
project_tag         = "{$this->config['project_tag']}";
project_rulesets[]  = "";

$custom_configs

INI;

        return $configIni;
    }

    private static function makeIniArray(string $name, array $array): string {
        return $name . '[] = "' . implode("\";\n{$name}[] = \"", $array) . "\";\n";
    }

    public function __get(string $name) {
        if (isset($this->config[$name])) {
            return $this->config[$name];
        } else {
            return null;
        }
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Phpexec;
use Exakat\Project;
use Exakat\Config as MainConfig;
use Exakat\Exceptions\NoPhpBinary;

class ExakatConfig extends Config {
    private $projects_root = '';

    private $gremlins = array( 'tinkergraph'   => 'Tinkergraph',
                               'tinkergraphv3' => 'TinkergraphV3',
                               'gsneo4j'       => 'GSNeo4j',
                               'gsneo4jv3'     => 'GSNeo4jV3',
                               'janusgraph'    => 'Janusgraph',
                               'nogremlin'     => 'NoGremlin',
                               'orientdb'      => 'Orientdb',
                               'bitsy'         => 'Bitsy',
                               );

    private $loaders = array( 'tinkergraph'   => 'SplitGraphson',
                              'tinkergraphv3' => 'SplitGraphson',
                              'gsneo4j'       => 'SplitGraphson',
                              'gsneo4jv3'     => 'SplitGraphson',
                              'janusgraph'    => 'SplitGraphson',
                              'orientdb'      => 'SplitGraphson',
                              'bitsy'         => 'SplitGraphson',
                              'nogremlin'     => 'None',
                              );

    // todo : list the authorized values here.

    public function __construct(string $projects_root) {
        $this->projects_root = $projects_root;
    }

    public function loadConfig(Project $project): ?string {
        // Default values
        $inis =  array(
                    array('graphdb'            => 'gsneo4jv3',
                          'gremlin'            => $this->gremlins['gsneo4jv3'],
                          'loader'             => $this->loaders['gsneo4jv3'],
                          'other_php_versions' => array(),
                          'transit_key'        => '',
                       )
                    );

        $configFiles = array("{$this->projects_root}/config/exakat.ini",
                             '/etc/exakat/exakat.ini',
                             '/etc/exakat.ini',
                             '', // This is the canary : when all fail, this will be used and returned
                             );

        // Attempt each init path, and stop at the first file we find
        $ini = null;
        foreach($configFiles as $configFile) {
            if (file_exists($configFile)) {
                // overwrite existing with the new, keep the default values
                $ini = @parse_ini_file($configFile);
                if (is_array($ini)) {
                    $inis = $ini + $inis[0];
                    break 1;
                } else {
                    $error = error_get_last();
                    print "Invalid config file '$configFile' : $error[message]Ignoring '$configFile'\n\n";
                }
            }
        }

        // Aliasing project_themes into rulesets
        if (isset($inis['project_themes'])) {
            print "Rename project_themes in project_rulesets, in your config/exakat.ini file\n";

            if (empty($inis['project_rulesets'])) {
                $inis['project_rulesets'] = $inis['project_themes'];
            }
        }
        $this->config = $inis;

        // Validation
        if (!isset($this->config['graphdb']) ||
            !in_array($this->config['graphdb'], array_keys($this->gremlins)) ) {
            display('Warning : No such graph as ' . ($this->config['graphdb'] ?? '') . " : using 'nogremlin', without graphdb.\n");
            $this->config['graphdb'] = 'nogremlin';
        }

        foreach(array_keys($this->gremlins) as $gdb) {
            $folder = "{$gdb}_folder";
            if (isset($this->config[$folder])) {
                if ($this->config[$folder][0] !== '/') {
                    $this->config[$folder] = "{$this->projects_root}/{$this->config[$folder]}";
                }
                $this->config[$folder] = realpath($this->config[$folder]);
            }
        }

        // Update values with actual loaders and gremlin
        $this->config['gremlin'] = $this->gremlins[$this->config['graphdb']];
        $this->config['loader']  = $this->loaders[$this->config['graphdb']];

        if (isset($this->config['concurencyCheck'])) {
            $this->config['concurencyCheck'] = (int) $this->config['concurencyCheck'];
            if ($this->config['concurencyCheck'] < 1024) {
                $this->config['concurencyCheck'] = 7610;
            } elseif ($this->config['concurencyCheck'] > 49150) {
                $this->config['concurencyCheck'] = 7610;
            }
        }

        foreach(MainConfig::PHP_VERSIONS as $version) {
            if (empty($this->config["php$version"])) {
                continue;
            }
            try {
                $php = new Phpexec("$version[0].$version[1]", $this->config["php$version"]);
            } catch (NoPhpBinary $e) {
                continue;
            }

            if ($php->isValid()) {
                $this->config['other_php_versions'][] = $version;
            }
        }

        if (isset($this->config['rules_version_max']) &&
            !preg_match('/^(\d+)\.(\d+)\.(\d+)$/', $this->config['rules_version_max'])) {
            $this->config['rules_version_max'] = '';
        }

        if (isset($this->config['rules_version_min']) &&
            !preg_match('/^(\d+)\.(\d+)\.(\d+)$/', $this->config['rules_version_min'])) {
            $this->config['rules_version_min'] = '';
        }

        $currentDir = getcwd();
        // Calculate the stubs recursivement if it is a folder
        // all path are absolute, may be placed anywhere
        if (isset($this->config['stubs'])) {
            $stubs = array(array());
            $this->config['stubs'] = makeArray($this->config['stubs']);
            foreach($this->config['stubs'] as $stub) {
                $path = realpath($stub);

                if ($path === false) {
                    continue;
                }

                if (!file_exists($path)) {
                    $stubs[$stub] = array();

                    continue;
                }

                if (is_file($path)) {
                    $stubs[$stub] = array($stub);

                    continue;
                }

                if (is_dir($path)) {
                    chdir($path);
                    $allFiles = rglob('.');
                    $allFiles = array_map(function (string $path) use ($stub): string { return $stub . ltrim($path, '.'); }, $allFiles);
                    chdir($currentDir);

                    $stubs[$stub] = $allFiles;
                }
            }
            $this->config['stubs'] = array_unique(array_merge(...array_values($stubs)));
        } else {
            $this->config['stubs'] = array();
        }

        return str_replace($currentDir, '.', $configFile);
    }

    public function __get(string $name) {
        if (isset($this->config[$name])) {
            return $this->config[$name];
        } else {
            return null;
        }

        return $return;
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Phpexec;
use Exakat\Project;
use Exakat\Config as Configuration;

class DotExakatConfig extends Config {
    private $dotExakat = '';

    public function __construct(Project $project, string $project_root) {
        if ($project->isDefault()) {
            $this->config['inside_code'] = Configuration::INSIDE_CODE;
            $this->dotExakat = getcwd() . '/.exakat.ini';
        } else {
            $this->config['inside_code'] = Configuration::WITH_PROJECTS;
            $this->dotExakat = $project_root . (string) $project . '/.exakat.ini';
        }

        $this->config['project'] = $project;
    }

    public function loadConfig(Project $project): ?string {
        if (!file_exists($this->dotExakat)) {
            $this->config['project']     = new Project();
            return self::NOT_LOADED;
        }

        $this->config = parse_ini_file($this->dotExakat);

        // removing empty values in the INI file
        foreach($this->config as &$value) {
            if (is_array($value) && empty($value[0])) {
                unset($value[0]);
            }
        }
        unset($value);

        if (isset($this->config['project_themes'])) {
            display("please, rename project_themes into project_rulesets in your .exakat.ini file\n");

            if (empty($this->config['project_rulesets'])) {
                $this->config['project_rulesets'] = $this->config['project_themes'];
            }
        }

        $other_php_versions = array();
        foreach(Configuration::PHP_VERSIONS as $version) {
            if (empty($this->config['php' . $version])) {
                continue;
            }
            $php = new Phpexec($version[0] . '.' . $version[1], $this->config["php$version"]);
            if ($php->isValid()) {
                $other_php_versions[] = $version;
            }
        }

        // check and default values
        $defaults = array( 'ignore_dirs'        => array('/test', '/tests', '/Tests', '/Test', '/example', '/examples', '/docs', '/doc', '/tmp', '/version', '/vendor', '/js', '/lang', '/data', '/css', '/cache', '/vendor', '/assets', '/spec', '/sql'),
                           'other_php_versions' => $other_php_versions,
                           'phpversion'         => substr(PHP_VERSION, 0, 3),
                           'file_extensions'    => array('php', 'php3', 'inc', 'tpl', 'phtml', 'tmpl', 'phps', 'ctp', 'module'),
                           'project_rulesets'   => 'CompatibilityPHP53,CompatibilityPHP54,CompatibilityPHP55,CompatibilityPHP56,CompatibilityPHP70,CompatibilityPHP71,CompatibilityPHP72,CompatibilityPHP73,CompatibilityPHP74,Dead code,Security,Analyze,Preferences,Appinfo,Appcontent',
                           'project_reports'    => array('Text'),
                        );

        foreach($defaults as $name => $value) {
            if (empty($this->config[$name])) {
                $this->config[$name] = $value;
            }
        }

        if (is_string($this->config['other_php_versions'])) {
            $this->config['other_php_versions'] = listToArray($this->config['other_php_versions']);
            foreach($this->config['other_php_versions'] as &$version) {
                $version = str_replace('.', '', trim($version));
            }
            unset($version);
        }

        if (is_string($this->config['file_extensions'])) {
            $this->config['file_extensions'] = listToArray($this->config['file_extensions']);
        }
        $this->config['file_extensions'] = $this->cleanFileExtensions($this->config['file_extensions']);

        if (is_string($this->config['project_reports'])) {
            $this->config['project_reports'] = listToArray($this->config['project_reports']);
            foreach($this->config['project_reports'] as &$ext) {
                $ext = trim($ext);
            }
            unset($ext);
        }

        if (is_string($this->config['project_rulesets'])) {
            $this->config['project_rulesets'] = listToArray($this->config['project_rulesets']);
            foreach($this->config['project_rulesets'] as &$ext) {
                $ext = trim($ext);
            }
            unset($ext);
        }

        return '.exakat.ini';
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Configsource;

use Exakat\Project as Project;

class EnvConfig extends Config {
    protected $config  = array();

    public function loadConfig(Project $project): ?string {
        if (!empty($e = getenv('EXAKAT_IGNORE_RULES'))) {
            $this->config['ignore_rules'] = str2array($e);
        }

        if (!empty($e = getenv('EXAKAT_IGNORE_DIRS'))) {
            $this->config['ignore_dirs'] = str2array($e);
        }

        if (!empty($e = getenv('EXAKAT_INCLUDE_DIRS'))) {
            $this->config['include_dirs'] = str2array($e);
        }

        if (!empty($e = getenv('EXAKAT_FILE_EXTENSIONS'))) {
            $this->config['file_extensions'] = str2array($e);
            $this->config['file_extensions'] = $this->cleanFileExtensions($this->config['file_extensions']);
        }

        if (!empty($e = getenv('EXAKAT_PROJECT_REPORTS'))) {
            $this->config['project_reports'] = str2array($e);
            $this->config['project_reports'] = $this->cleanProjectReports($this->config['project_reports']);
        }

        if (!empty($e = getenv('EXAKAT_PROJECT_RULESETS'))) {
            $this->config['project_rulesets'] = str2array($e);
            $this->config['project_rulesets'] = $this->cleanProjectReports($this->config['project_rulesets']);
        }

        return empty($this->config) ? self::NOT_LOADED : 'environnment';
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;


class CakePHP {
    private $sqlite = null;
    private $phar_tmp = null;

    public function __construct($path, $is_phar) {
        if ($is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), 'excakephp') . '.sqlite';
            copy($path . '/cakephp.sqlite', $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            $docPath = $path . '/cakephp.sqlite';
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null) {
            unlink($this->phar_tmp);
        }
    }

    public function getVersions() {
        $query = 'SELECT DISTINCT replace(release, "release-","") AS version FROM releases ORDER BY 1';
        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_NUM)) {
            $return[] = $row[0];
        }

        return $return;
    }

    public function getClasses($component = 'cakephp', $release = null) {
        $query = 'SELECT namespaces.namespace || "\" || class AS class, release FROM classes 
                    JOIN namespaces 
                      ON classes.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id 
                    JOIN components 
                      ON releases.component_id = components.id 
                    WHERE components.component = "' . $component . '"';
        if ($release !== null) {
            $query .= " AND releases.release = \"$release.0\"";
        }

        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['class'];
            } else {
                $return[$row['release']] = array($row['class']);
            }
        }

        return $return;
    }

    public function getInterfaces($component = 'cakephp', $release = null) {
        $query = 'SELECT namespaces.namespace || "\" || interface AS interface, release FROM interfaces 
                    JOIN namespaces 
                      ON interfaces.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id
                    JOIN components 
                      ON releases.component_id = components.id 
                    WHERE components.component = "' . $component . '"';
        if ($release !== null) {
            $query .= " AND releases.release = \"$release.0\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['interface'];
            } else {
                $return[$row['release']] = array($row['interface']);
            }
        }


        return $return;
    }

    public function getTraits($component = 'cakephp', $release = null) {
        $query = 'SELECT namespaces.namespace || "\" || trait AS trait, release FROM traits 
                    JOIN namespaces 
                      ON traits.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id
                    JOIN components 
                      ON releases.component_id = components.id 
                    WHERE components.component = "' . $component . '"';
        if ($release !== null) {
            $query .= " AND releases.release = \"$release.0\"";
        }
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['trait'];
            } else {
                $return[$row['release']] = array($row['trait']);
            }
        }

        return $return;
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Data;


class Dictionary {
    public const CASE_SENSITIVE   = true;
    public const CASE_INSENSITIVE = false;

    private $datastore  = null;
    private $dictionary = array();
    private $lcindex    = array();

    public function __construct() {
        $this->datastore = exakat('datastore');
    }

    private function init(): void {
        $this->dictionary = $this->datastore->getAllHash('dictionary');
        foreach(array_keys($this->dictionary) as $key) {
            $this->lcindex[mb_strtolower((string) $key)] = 1;
        }
    }

    public function translate(array $code, bool $case = self::CASE_SENSITIVE): array {
        if (empty($this->dictionary)) {
            $this->init();
        }
        $return = array();

        if ($case === self::CASE_SENSITIVE) {
            $caseClosure = function (string $x) { return $x; };
        } else {
            $caseClosure = function (string $x) { return mb_strtolower($x); };
        }

        foreach($code as $c) {
            $d = $caseClosure($c);
            if (isset($this->dictionary[$d])) {
                $return[] = $this->dictionary[$d];
            }
        }

        return $return;
    }

    public function read(array $code): array {
        if (empty($this->dictionary)) {
            $this->init();
        }

        $return = array();
        foreach($code as $c) {
            $d = array_search($c, $this->dictionary);
            if ($d !== false) {
                $return[$c] = $d;
            }
        }

        return $return;
    }

    public function grep(string $regex): array {
        $keys = preg_grep($regex, array_keys($this->dictionary));

        $return = array();
        foreach($keys as $k) {
            $return[] = $this->dictionary[$k];
        }

        return $return;
    }

    public function source(array $code): array {
        $return = array();

        $reverse = array_flip($this->dictionary);

        foreach($code as $c) {
            if (isset($reverse[$c])) {
                $return[] = $reverse[$c];
            }
        }

        return $return;
    }

    public function length(string $length): array {
        $return = array();

        if (preg_match('/ > (\d+)/', $length, $r)) {
            $closure = function (string $s) use ($r) { return strlen($s) > $r[1]; };
        } elseif (preg_match('/ == (\d+)/', $length, $r)) {
            $closure = function (string $s) use ($r) { return strlen($s) === (int) $r[1]; };
        } elseif (preg_match('/ < (\d+)/', $length, $r)) {
            $closure = function (string $s) use ($r) { return strlen($s) < $r[1]; };
        } else {
            assert(false, "codeLength didn't understand $length");
        }

        $return = array_filter($this->dictionary, $closure, ARRAY_FILTER_USE_KEY);

        return array_values($return);
    }

    public function staticMethodStrings(): array {
        $doublecolon = array_filter($this->dictionary, function ($x) { return strlen($x) > 6 &&
                                                                              strpos($x,' ') === false &&
                                                                              strpos($x,'::') !== false &&
                                                                              mb_strtolower($x) === $x;},
                                                                              ARRAY_FILTER_USE_KEY );

        $return = array();
        foreach($doublecolon as $key => $value) {
            // how can this regex fail ?
            if (preg_match('/^[\'"](.+?)::(.+?)/', $key, $r)) {
                $return['\\' . $r[1]] = $value;
            }
        }

        return $return;
    }
}
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;

class Collector {
    private $dictionary = array();
    private $last       = array();
    private $count = 0;

    public function get(string $v): int {
        if (isset($this->dictionary[$v])) {
            return $this->dictionary[$v];
        }

        ++$this->count;
        $this->dictionary[$v] = $this->count;
        $this->last[$v] = $this->count;

        return $this->count;
    }

    public function getDictionary(): array {
        return $this->dictionary;
    }

    public function getRecent(): array {
        $last = $this->last;
        $this->last = array();

        return $last;
    }
}
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;


class ZendF {
    private $sqlite = null;
    private $phar_tmp = null;

    public function __construct($path, $is_phar) {
        if ($is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), 'exzendf') . '.sqlite';
            copy("$path/zendf.sqlite", $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            $docPath = "$path/zendf.sqlite";
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null) {
            unlink($this->phar_tmp);
        }
    }

    public function getClassByRelease($release = null) {
        $query = 'SELECT class, release FROM classes 
                    JOIN namespaces 
                      ON classes.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id';
        if ($release !== null) {
            $query .= " WHERE releases.release = \"release-$release.0\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['class'];
            } else {
                $return[$row['release']] = array($row['class']);
            }
        }

        return $return;
    }

    public function getInterfaceByRelease($release = null) {
        $query = 'SELECT interface, release FROM interfaces 
                    JOIN namespaces 
                      ON interfaces.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id';
        $res = $this->sqlite->query($query);
        $return = array();

        if ($release !== null) {
            $return = array($release => array());
            $query .= " WHERE releases.release = \"release-$release.0\"";
        }

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['interface'];
            } else {
                $return[$row['release']] = array($row['interface']);
            }
        }

        return $return;
    }

    public function getTraitByRelease($release = null) {
        $query = 'SELECT trait, release FROM traits 
                    JOIN namespaces 
                      ON traits.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id';
        $res = $this->sqlite->query($query);
        $return = array();

        if ($release !== null) {
            $return = array($release => array());
            $query .= " WHERE releases.release = \"release-$release.0\"";
        }

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['trait'];
            } else {
                $return[$row['release']] = array($row['trait']);
            }
        }

        return $return;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;


class ZendF3 {
    private $sqlite = null;
    private $phar_tmp = null;

    public function __construct($path, $is_phar) {
        if ($is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), 'exzendf3') . '.sqlite';
            copy($path . '/zendf3.sqlite', $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            $docPath = $path . '/zendf3.sqlite';
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null) {
            unlink($this->phar_tmp);
        }
    }

    public function getVersions($component = null) {
        $query = 'SELECT DISTINCT replace(release, "release-","") AS version FROM releases';
        if ($component !== null) {
            $query .= '  JOIN components 
                      ON releases.component_id = components.id 
 WHERE components.component = "' . $component . '"';
        }
        $query .= ' ORDER BY 1';
        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_NUM)) {
            $return[] = $row[0];
        }

        return $return;
    }

    public function getClasses($component, $release = null) {
        $query = 'SELECT namespaces.namespace || "\" || class AS class, release FROM classes 
                    JOIN namespaces 
                      ON classes.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id 
                    JOIN components 
                      ON releases.component_id = components.id 
                    WHERE components.component = "' . $component . '"';
        if ($release !== null) {
            $query .= " AND releases.release = \"release-$release.0\"";
        }

        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            array_collect_by($return, $row['release'], $row['class']);
        }

        return $return;
    }

    public function getInterfaces($component, $release = null) {
        $query = 'SELECT namespaces.namespace || "\" || interface AS interface, release FROM interfaces 
                    JOIN namespaces 
                      ON interfaces.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id
                    JOIN components 
                      ON releases.component_id = components.id 
                    WHERE components.component = "' . $component . '"';
        if ($release !== null) {
            $query .= " AND releases.release = \"release-$release.0\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            array_collect_by($return, $row['release'], $row['interface']);
        }

        return $return;
    }

    public function getTraits($component, $release = null) {
        $query = 'SELECT namespaces.namespace || "\" || trait AS trait, release FROM traits 
                    JOIN namespaces 
                      ON traits.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id
                    JOIN components 
                      ON releases.component_id = components.id 
                    WHERE components.component = "' . $component . '"';
        if ($release !== null) {
            $query .= " AND releases.release = \"release-$release.0\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            array_collect_by($return, $row['release'], $row['trait']);
        }

        return $return;
    }

    public function getDeprecated($component = null, $release = null) {
        $where = array();
        if ($component !== null) {
            $where[] = 'components.component = "' . $component . '"';
        }

        if ($release !== null) {
            $where[] = "releases.release = \"release-$release.0\"";
        }

        if (empty($where)) {
            $whereSQL = '';
        } else {
            $whereSQL = ' WHERE ' . implode(' AND ', $where);
        }


        $query = <<<SQL
SELECT type, cit, name, namespaces.namespace, release FROM deprecated 
    JOIN namespaces 
      ON deprecated.namespace_id = namespaces.id
    JOIN releases 
      ON namespaces.release_id = releases.id
    JOIN components 
      ON releases.component_id = components.id 
    $whereSQL
    GROUP BY type, cit, name
SQL;

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $type = $row['type'];
            unset($row['type']);

            $release = $row['release'];
            unset($row['release']);

            if (isset($return[$type][$release])) {
                $return[$type][$release][] = $row;
            } elseif (isset($return[$type])) {
                $return[$type][$release] = array($row);
            } else {
                $return[$type] = array($release => array($row));
            }
        }

        return $return;
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;

class Composer {
    private $sqlite = null;
    private $phar_tmp = null;

    public function __construct($config) {
        if ($config->is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), 'exMethods') . '.sqlite';
            copy($config->dir_root . '/data/composer.sqlite', $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            $docPath = $config->dir_root . '/data/composer.sqlite';
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null) {
            unlink($this->phar_tmp);
        }
    }

    public function getComposerNamespaces($vendor = null) {
        $query = "SELECT namespace AS namespace FROM namespaces WHERE namespace != 'global' ";
        if ($vendor !== null) {
            list($vendor, $component) = explode('/', $vendor);
            $query .= " AND vendor = '$vendor' AND component = '$component'";

        }
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = strtolower($row['namespace']);
        }

        return $return;
    }

    public function getComposerClasses() {
        // global namespace is stored with 'global' keyword, so we remove it.
        $query = "SELECT DISTINCT CASE namespace WHEN 'global' THEN classname ELSE namespace || '\\' || classname END AS classname 
        FROM namespaces 
        JOIN classes 
            ON classes.namespace_id = namespaces.id";

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = strtolower($row['classname']);
        }

        return $return;
    }

    public function getComposerInterfaces($vendor = null) {
        // global namespace is stored with 'global' keyword, so we remove it.
        $query = "SELECT CASE namespace WHEN 'global' THEN interfacename ELSE namespace || '\\' || interfacename END AS interfacename FROM namespaces 
JOIN interfaces ON interfaces.namespace_id = namespaces.id";
        if ($vendor !== null) {
            list($vendor, $component) = explode('/', $vendor);
            $query .= " WHERE vendor = '$vendor' and component = '$component'";

        }
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = strtolower($row['interfacename']);
        }

        return $return;
    }

    public function getComposerTraits($vendor = null) {
        // global namespace is stored with 'global' keyword, so we remove it.
        $query = "SELECT CASE namespace WHEN 'global' THEN traitname ELSE namespace || '\\' || traitname END AS traitname FROM namespaces 
JOIN traits ON traits.namespace_id = namespaces.id";
        if ($vendor !== null) {
            list($vendor, $component) = explode('/', $vendor);
            $query .= " WHERE vendor = '$vendor' and component = '$component'";

        }
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = strtolower($row['traitname']);
        }

        return $return;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;


class ZendF2 {
    private $sqlite = null;
    private $phar_tmp = null;

    public function __construct($path, $config) {
        if ($config->is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), 'exzendf2') . '.sqlite';
            copy($path . '/zendf2.sqlite', $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            $docPath = $path . '/zendf2.sqlite';
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null) {
            unlink($this->phar_tmp);
        }
    }

    public function getClassByRelease($release = null) {
        $query = 'SELECT namespaces.namespace || "\" || class AS class, release FROM classes 
                    JOIN namespaces 
                      ON classes.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id';
        if ($release !== null) {
            $query .= " WHERE releases.release = \"release-$release.0\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['class'];
            } else {
                $return[$row['release']] = array($row['class']);
            }
        }

        return $return;
    }

    public function getInterfaceByRelease($release = null) {
        $query = 'SELECT namespaces.namespace || "\" || interface AS interface, release FROM interfaces 
                    JOIN namespaces 
                      ON interfaces.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id';
        $res = $this->sqlite->query($query);
        $return = array();

        if ($release !== null) {
            $query .= " WHERE releases.release = \"release-$release.0\"";
        }

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['interface'];
            } else {
                $return[$row['release']] = array($row['interface']);
            }
        }

        return $return;
    }

    public function getTraitByRelease($release = null) {
        $query = 'SELECT namespaces.namespace || "\" || trait AS trait, release FROM traits 
                    JOIN namespaces 
                      ON traits.namespace_id = namespaces.id
                    JOIN releases 
                      ON namespaces.release_id = releases.id';
        $res = $this->sqlite->query($query);
        $return = array();

        if ($release !== null) {
            $query .= " WHERE releases.release = \"release-$release.0\"";
        }

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['release']])) {
                $return[$row['release']][] = $row['trait'];
            } else {
                $return[$row['release']] = array($row['trait']);
            }
        }

        return $return;
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;


abstract class Data {
    private $config = null;

    protected $name = '';

    private $sqlite = null;
    private $phar_tmp = null;

    public function __construct(string $name) {
        $this->name = $name;
        $this->config = exakat('config');

        $fullpath = $this->config->dir_root . "/data/$name.sqlite";
        if ($this->config->is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), $name) . '.sqlite';
            if (file_exists($fullpath)) {
                copy($fullpath, $this->phar_tmp);
            } elseif (($this->config->ext !== null) && $this->config->ext->fileExists("data/$name.sqlite") ) {
                $this->config->ext->copyFile("data/$name.sqlite", $this->phar_tmp);
            } else {
                assert(false, "No database for '$name.sqlite'.");
            }
            $docPath = $this->phar_tmp;
        } elseif (file_exists($fullpath)) {
            $docPath = $fullpath;
        } elseif (($this->config->ext !== null) && $this->config->ext->fileExists("data/$name.sqlite") ) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), $name) . '.sqlite';
            $this->config->ext->copyFile("data/$name.sqlite", $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            assert(false, "No database for '$name.sqlite'.");
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null) {
            unlink($this->phar_tmp);
        }
    }

    public function getVersions($component = null) {
        $query = 'SELECT version AS version FROM versions ORDER BY 1';
        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_NUM)) {
            $return[] = $row[0];
        }

        return $return;
    }

    public function getCIT($component, $version = null) {
        $query = 'SELECT namespaces.name || "\" || cit.name AS className, version FROM cit 
                    JOIN namespaces 
                      ON cit.namespaceId = namespaces.id
                    JOIN versions 
                      ON namespaces.versionId = versions.id ';
        if ($version !== null) {
            $query .= " WHERE versions.version = \"$version\"";
        }

        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['version']])) {
                $return[$row['version']][] = $row['className'];
            } else {
                $return[$row['version']] = array($row['className']);
            }
        }

        return $return;
    }

    public function getClasses($component, $version = null) {
        $query = 'SELECT namespaces.name || "\" || cit.name AS className, version FROM cit 
                    JOIN namespaces 
                      ON cit.namespaceId = namespaces.id
                    JOIN versions 
                      ON namespaces.versionId = versions.id 
                    WHERE cit.type = "class"';
        if ($version !== null) {
            $query .= " AND versions.version = \"$version\"";
        }

        $res = $this->sqlite->query($query);

        $return = array();
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['version']])) {
                $return[$row['version']][] = $row['className'];
            } else {
                $return[$row['version']] = array($row['className']);
            }
        }

        return $return;
    }

    public function getInterfaces($component, $version = null) {
        $query = 'SELECT namespaces.name || "\" || cit.name AS interfaceName, version FROM cit 
                    JOIN namespaces 
                      ON cit.namespaceId = namespaces.id
                    JOIN versions 
                      ON namespaces.versionId = versions.id 
                    WHERE cit.type = "interface"';
        if ($version !== null) {
            $query .= " AND versions.version = \"$version\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['version']])) {
                $return[$row['version']][] = $row['interfaceName'];
            } else {
                $return[$row['version']] = array($row['interfaceName']);
            }
        }

        return $return;
    }

    public function getTraits($component, $version = null) {
        $query = 'SELECT namespaces.name || "\" || cit.name AS traitName, version FROM cit 
                    JOIN namespaces 
                      ON cit.namespaceId = namespaces.id
                    JOIN versions 
                      ON namespaces.versionId = versions.id 
                    WHERE cit.type = "trait" AND
                          namespaces.name != "" ';
        if ($version !== null) {
            $query .= " AND versions.version = \"$version\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['version']])) {
                $return[$row['version']][] = $row['traitName'];
            } else {
                $return[$row['version']] = array($row['traitName']);
            }
        }

        return $return;
    }

    public function getNamespaces($component, $version = null) {
        $query = 'SELECT namespaces.name as namespaceName, version FROM namespaces 
                    JOIN versions 
                      ON namespaces.versionId = versions.id 
                  WHERE namespaces.name != "" ';
        if ($version !== null) {
            $query .= " AND versions.version = \"$version\"";
        }

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (isset($return[$row['version']])) {
                $return[$row['version']][] = $row['namespaceName'];
            } else {
                $return[$row['version']] = array($row['namespaceName']);
            }
        }

        return $return;
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Data;

use Exakat\Config;

class Methods {
    private $sqlite = null;
    private $phar_tmp = null;

    public const STRICT = true;
    public const LOOSE  = false;

    public const INVERSE  = false;
    public const DIRECT   = true;

    private const ARGS_COL = array('arg0', 'arg1', 'arg2', 'arg3', 'arg4', 'arg5', 'arg6', 'arg7', 'arg8', 'arg9', 'arg10', 'arg11');

    public function __construct(Config $config) {
        if ($config->is_phar) {
            $this->phar_tmp = tempnam(sys_get_temp_dir(), 'exMethods') . '.sqlite';
            copy($config->dir_root . '/data/methods.sqlite', $this->phar_tmp);
            $docPath = $this->phar_tmp;
        } else {
            $docPath = $config->dir_root . '/data/methods.sqlite';
        }
        $this->sqlite = new \Sqlite3($docPath, \SQLITE3_OPEN_READONLY);
    }

    public function __destruct() {
        if ($this->phar_tmp !== null && file_exists($this->phar_tmp)) {
            unlink($this->phar_tmp);
        }
    }

    public function getPhpFunctions(): array {
        $query = 'SELECT name FROM methods WHERE class = "PHP"';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getPhpClasses(): array {
        $query = 'SELECT DISTINCT class FROM methods WHERE class != "PHP"';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getMethodsArgsInterval(): array {
        $query = 'SELECT class, name, args_min, args_max FROM methods WHERE class != "PHP"';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getFunctionsArgsInterval(): array {
        $query = 'SELECT class, name, args_min, args_max FROM methods WHERE Class = "PHP"';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getNewArgsInterval(): array {
        $query = 'SELECT class, args_min, args_max FROM methods WHERE Class != "PHP" AND name = "__construct"';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getFunctionsLastArgsNotBoolean(): array {
        $clauses = array();
        foreach(self::ARGS_COL as $position => $name) {
            $max = $position + 1;
            $clauses[] = "(args_max = $max AND not instr(arg$position, 'bool') AND arg$position != '')";
        }

        $query = 'SELECT \'\' || lower(methods.name) AS fullnspath, args_max - 1 AS position FROM methods 
JOIN args_type ON args_type.name = methods.name
WHERE methods.class = "PHP" AND
     ' . implode(' OR ', $clauses);
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row['fullnspath'];
        }

        return $return;
    }

    public function getFunctionsReferenceArgs(): array {
        $clauses = array();
        foreach(self::ARGS_COL as $position => $name) {
            $clauses[] = "SELECT name AS function, $position AS position FROM args_is_ref WHERE Class = 'PHP' AND arg$position = 'reference'";
        }

        $query = implode(' UNION ', $clauses);

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getFunctionsValueArgs(): array {
        $clauses = array();
        foreach(self::ARGS_COL as $position => $name) {
            $clauses[] = "SELECT name AS function, $position AS position FROM args_is_ref WHERE Class = 'PHP' AND arg$position = 'value'";
        }

        $query = implode(' UNION ', $clauses);

        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getDeterministFunctions(): array {
        $query = 'SELECT name FROM methods WHERE determinist = 1';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row['name'];
        }

        return $return;
    }

    public function getNonDeterministFunctions(): array {
        $query = 'SELECT name FROM methods WHERE determinist = 0';
        $res = $this->sqlite->query($query);
        $return = array();

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row['name'];
        }

        return $return;
    }

    public function getNativeMethodArgType(): array {
        $return = array();

            $query = <<<'SQL'
SELECT lower(class) as class, lower(name) AS name, arg0, arg1 FROM args_type WHERE class != 'PHP' AND arg0 != ''
SQL;
        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (!isset($return['\\' . $row['class']])) {
                $return['\\' . $row['class']] = array();
            }

            foreach(range(0, 11) as $i) {
                if (empty($row['arg' . $i])) {
                    continue;
                }

                $types = explode(',', $row['arg' . $i]);
                foreach($types as &$type) {
                    if ($type[0] !== '\\') {
                        $type = '\\' . $type;
                    }
                }
                unset($type);
                $return['\\' . $row['class']][$row['name']][$i] = $types;
            }
        }

        return $return;
    }

    public function getNativeMethodReturn(): array {
        $return = array();

        $query = <<<'SQL'
SELECT lower(class) AS class, 
       lower(name) AS name, 
       return 
       FROM args_type 
       WHERE class != 'PHP' AND 
             return != ''
SQL;
        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            if (!isset($return['\\' . $row['class']])) {
                $return['\\' . $row['class']] = array();
            }

            $types = explode(',', $row['return']);
            foreach($types as &$type) {
                if ($type[0] !== '\\') {
                    $type = '\\' . $type;
                }
            }
            unset($type);
            $return['\\' . $row['class']][$row['name']] = $types;
       }

        return $return;
    }

    public function getInternalParameterType(): array {
        $return = array();

        $args = self::ARGS_COL;
        foreach($args as $id => $arg) {
            $query = <<<SQL
SELECT $arg, lower(GROUP_CONCAT('\' || name)) AS functions FROM args_type WHERE class='PHP' AND $arg IN ('int', 'array', 'bool','string') GROUP BY $arg
SQL;
            $res = $this->sqlite->query($query);

            $position = array();
            while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
                $position[$row[$arg]] = explode(',', $row['functions']);
            }

            $return[$id] = $position;
        }

        return $return;
    }

    public function getInternalParameterNames(array $function): array {
        $return = array();

        $list = makeList($function);
        $query = <<<SQL
SELECT name, arg0, arg1, arg2, arg3, arg4, arg5, arg5, arg6, arg7, arg8, arg9, arg10, arg11 FROM args_names WHERE class='PHP' AND name in ($list);
SQL;
        $res = $this->sqlite->query($query);

        $return = array();
        while ($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return['\\' . $row['name']] = $row;
        }

        return $return;
    }

    public function getFunctionsByArgType(string $typehint = 'int', $strict = self::STRICT): array {
        $return = array_fill(0, 10, array());

        if ($strict === self::LOOSE) {
            $search = " LIKE '%$typehint%'";
        } elseif ($strict === self::STRICT) {
            $search = " = '$typehint'";
        } else {
            // Default is strict
            $search = " = '$typehint'";
        }

        $clauses = array();
        foreach(self::ARGS_COL as $position => $name) {
            $max = $position + 1;
            $clauses[] = "SELECT name AS function, $position AS position FROM args_type WHERE Class = 'PHP' AND arg$position $search";
        }

        $query = implode(' UNION ', $clauses);

        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            array_collect_by($return, (int) $row['position'], '\\' . mb_strtolower($row['function']));
        }

        return $return;
    }

    public function getBugFixes(): array {
        $return = array();

        $query = <<<'SQL'
SELECT * FROM bugfixes ORDER BY SUBSTR(solvedIn72, 5) + 0 DESC, SUBSTR(solvedIn71, 5) + 0 DESC, SUBSTR(solvedIn70, 5) + 0 DESC, SUBSTR(56, 5) + 0 DESC 
SQL;
        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = $row;
        }

        return $return;
    }

    public function getFunctionsByReturn(bool $singleTypeOnly = self::LOOSE): array {
        $return = array();

        if ($singleTypeOnly === true) {
            $where = ' AND return NOT LIKE "%,%"';
        } else {
            $where = '';
        }

        $query = <<<SQL
SELECT return, lower(GROUP_CONCAT('\' || name)) AS functions 
    FROM args_type 
    WHERE class='PHP'         AND 
          return IS NOT NULL $where
    GROUP BY return
SQL;
        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $types = explode(',', $row['return']);
            foreach($types as $type) {
                array_collect_by($return, $type, explode(',', $row['functions']));
            }
        }

        foreach($return as &$list) {
            $list = array_merge(...$list);
        }
        unset($list);

        return $return;
    }

    public function getFunctionsByReturnType(string $type = 'int', bool $singleTypeOnly = self::STRICT): array {
        $return = array();

        if ($singleTypeOnly === self::STRICT) {
            $where = ' AND return NOT LIKE "%,%"';
        } else {
            $where = '';
        }

        $query = <<<SQL
SELECT return, lower(GROUP_CONCAT('\' || name)) AS functions 
    FROM args_type 
    WHERE class='PHP'         AND 
          return LIKE '%$type%' AND
          return IS NOT NULL $where
    GROUP BY return
SQL;
        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $return[] = explode(',', $row['functions']);
        }

        $return = array_merge(...$return);

        return $return;
    }

    public function getArgsByType(string $type = 'int', bool $not = self::DIRECT): array {
        $return = array_fill(0, 12, array());

        $not = $not === self::DIRECT ? '' : ' NOT ';

        $query = <<<SQL
SELECT CASE WHEN arg0  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg0 ,
       CASE WHEN arg1  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg1 ,
       CASE WHEN arg2  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg2 ,
       CASE WHEN arg3  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg3 ,
       CASE WHEN arg4  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg4 ,
       CASE WHEN arg5  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg5 ,
       CASE WHEN arg6  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg6 ,
       CASE WHEN arg7  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg7 ,
       CASE WHEN arg8  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg8 ,
       CASE WHEN arg9  $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg9 ,
       CASE WHEN arg10 $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg10,
       CASE WHEN arg11 $not LIKE '%$type%' THEN 1 ELSE 0 END AS arg11,
       CASE WHEN class = 'PHP' THEN lower('\' || name) ELSE lower('\' || class || '::' || name) END AS function
    FROM args_type
SQL;
        $res = $this->sqlite->query($query);

        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $function = $row['function'];
            unset($row['function']);
            foreach($row as $arg => $typed) {
                $arg = $arg[3];
                if (!empty($typed)) {
                    $return[$arg][] = $function;
                }
            }
        }

        return $return;
    }
}

?>
<?php declare(strict_types = 1);

namespace Exakat\Dump;

use Exakat\Reports\Helpers\Results;

class Dump1 extends Dump {
    protected function initDump(): void {
        $query = <<<'SQL'
CREATE TABLE themas (  id    INTEGER PRIMARY KEY AUTOINCREMENT,
                       thema STRING,
                       CONSTRAINT "themas" UNIQUE (thema) ON CONFLICT IGNORE
                    )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE results (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                        fullcode STRING,
                        file STRING,
                        line INTEGER,
                        namespace STRING,
                        class STRING,
                        function STRING,
                        analyzer STRING,
                        severity STRING
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE resultsCounts ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                             analyzer STRING,
                             count INTEGER DEFAULT -6,
                             CONSTRAINT "analyzers" UNIQUE (analyzer) ON CONFLICT REPLACE
                           )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE hashAnalyzer ( id INTEGER PRIMARY KEY,
                            analyzer STRING,
                            key STRING UNIQUE,
                            value STRING
                          );
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE hashResults ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                            name STRING,
                            key STRING,
                            value STRING
                          );
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE classChanges (  
    id           INTEGER PRIMARY KEY AUTOINCREMENT,
    changeType   STRING,
    name         STRING,
    parentClass  STRING,
    parentValue  STRING,
    childClass   STRING,
    childValue   STRING
                    )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE filesDependencies ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                                 including STRING,
                                 included STRING,
                                 type STRING
                                )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE classesDependencies ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                                   including STRING,
                                   including_name STRING,
                                   including_type STRING,
                                   included STRING,
                                   included_name STRING,
                                   included_type STRING,
                                   type STRING
                                  )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE atomsCounts (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                            atom STRING,
                            count INTEGER
                         )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE phpStructures (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                              type STRING,
                              name STRING,
                              count INTEGER
)
SQL;
        $this->sqlite->query($query);

        // Name spaces
        $query = <<<'SQL'
CREATE TABLE namespaces (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                           namespace STRING
                        )
SQL;
        $this->sqlite->query($query);
        $this->sqlite->query("INSERT INTO namespaces VALUES (1, '\\')");

        $query = <<<'SQL'
CREATE TABLE cit (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name STRING,
                    namespaceId INTEGER DEFAULT 1,
                    type STRING,
                    abstract INTEGER,
                    final INTEGER,
                    phpdoc STRING,
                    begin INTEGER,
                    end INTEGER,
                    file INTEGER,
                    line INTEGER,
                    extends STRING DEFAULT ""
                  )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE cit_implements (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                               implementing INTEGER,
                               implements STRING,
                               type    STRING
                            )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE methods (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                        method INTEGER,
                        citId INTEGER,
                        static INTEGER,
                        final INTEGER,
                        abstract INTEGER,
                        visibility STRING,
                        returntype STRING,
                        phpdoc STRING,
                        begin INTEGER,
                        end INTEGER
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE arguments (id INTEGER PRIMARY KEY AUTOINCREMENT,
                        name STRING,
                        citId INTEGER,
                        methodId INTEGER,
                        rank INTEGER,
                        reference INTEGER,
                        variadic INTEGER,
                        init STRING,
                        line INTEGER,
                        typehint STRING
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE properties (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                           property INTEGER,
                           citId INTEGER,
                           visibility STRING,
                           static INTEGER,
                           phpdoc STRING,
                           value STRING
                           )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE classconstants ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                              constant INTEGER,
                              citId INTEGER,
                              visibility STRING,
                              phpdoc STRING,
                              value STRING
                            )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE constants (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                          constant INTEGER,
                          namespaceId INTEGER,
                          file STRING,
                          value STRING,
                          phpdoc STRING,
                          type STRING
                       )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE functions (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                          function STRING,
                          type STRING,
                          namespaceId INTEGER,
                          returntype STRING,
                          reference INTEGER,
                          file STRING,
                          phpdoc STRING,
                          begin INTEGER,
                          end INTEGER,
                          line INTEGER,
                          CONSTRAINT "unique" UNIQUE (function, line)
)
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE readability ( id      INTEGER PRIMARY KEY AUTOINCREMENT,
                           name    STRING,
                           type    STRING,
                           tokens  INTEGER,
                           expressions INTEGER,
                           file        STRING
                         )
SQL;
        $this->sqlite->query($query);


        $query = <<<'SQL'
CREATE TABLE variables (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                          variable STRING,
                          type STRING
                       )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE globalVariables ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                               variable STRING,
                               file STRING,
                               line INTEGER,
                               isRead INTEGER,
                               isModified INTEGER,
                               type STRING
                             )
SQL;
        $this->sqlite->query($query);

        $this->collectDatastore();
        $this->initTablesList();

        $time   = time();
        try {
            $id     = random_int(0, PHP_INT_MAX);
        } catch (\Throwable $e) {
            die("Couldn't generate an id for the current dump file. Aborting");
        }

        if (file_exists($this->sqliteFilePrevious)) {
            $sqliteOld = new \Sqlite3($this->sqliteFilePrevious);
            $sqliteOld->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

            $presence = $sqliteOld->querySingle('SELECT count(*) FROM sqlite_master WHERE type="table" AND name="hash"');
            if ($presence == 1) {
                $serial = $sqliteOld->querySingle('SELECT value FROM hash WHERE key="dump_serial"') + 1;
            } else {
                $serial = 0;
            }
        } else {
            $serial = 1;
        }

        $toDump = array(array('', 'dump_time',   $time),
                        array('', 'dump_id',     $id),
                        array('', 'dump_serial', $serial),
                        );

        $this->storeInTable('hash', $toDump);
        display('Inited tables');
    }

    public function fetchAnalysers(array $analysers): Results {
        $query = 'SELECT fullcode, file, line, analyzer, class, namespace, function FROM results WHERE analyzer IN (' . makeList($analysers) . ')';
        $res = $this->sqlite->query($query);

        return new Results($res, array('phpsyntax' => array('fullcode' => 'htmlcode')));
    }

    public function fetchAnalysersCounts(array $analysers): Results {
        $query = 'SELECT analyzer, count FROM resultsCounts WHERE analyzer IN (' . makeList($analysers) . ')';
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function fetchTable(string $table, array $cols = array()): Results {
        if (empty($cols)) {
            $cols = '*';
        } else {
            $list = array();
            foreach($cols as $k => $col) {
                if (is_int($k)) {
                    $list[] = $col;
                } else {
                    $list[] = "$col as $k";
                }
            }
            $cols = implode(', ', $list);
        }

        if (!in_array($table, $this->tablesList)) {
            return new Results();
        }

        $query = "SELECT $cols FROM $table";
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function getExtensionList(): Results {
        $query = <<<'SQL'
SELECT analyzer, count(*) AS count FROM results 
    WHERE analyzer LIKE "Extensions/Ext%"
    GROUP BY analyzer
    ORDER BY count(*) DESC
SQL;

        return $this->query($query);
    }

    public function fetchHash(string $key): Results {
        $query = <<<SQL
SELECT value FROM hash WHERE key = "$key"
SQL;

        return $this->query($query);
    }

    public function fetchHashResults(string $key): Results {
        $query = <<<SQL
SELECT key, value FROM hashResults
WHERE name = "$key"
ORDER BY key + 0
SQL;

        return $this->query($query);
    }

    public function getCit($type = 'class'): Results {
        assert(in_array($type, array('class', 'trait', 'interface')));

        $query = "SELECT name FROM cit WHERE type='$type' ORDER BY name";

        return $this->query($query);
    }

    private function query(string $query): Results {
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function fetchTableFunctions(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT functions.*, 
GROUP_CONCAT((CASE arguments.typehint WHEN ' ' THEN '' ELSE arguments.typehint || ' ' END ) || 
              CASE arguments.reference WHEN 0 THEN '' ELSE '&' END || 
              CASE arguments.variadic WHEN 0 THEN '' ELSE '...' END  || arguments.name || 
              (CASE arguments.init WHEN ' ' THEN '' ELSE ' = ' || arguments.init END),
             ', ' ) AS signature

FROM functions

LEFT JOIN arguments
    ON functions.id = arguments.methodId AND
       arguments.citId = 0
GROUP BY functions.id

SQL
        );

        return new Results($res);
    }

    public function fetchTableMethods(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT methods.*, 
       GROUP_CONCAT((CASE arguments.typehint WHEN ' ' THEN '' ELSE arguments.typehint || ' ' END ) || 
                     CASE arguments.reference WHEN 0 THEN '' ELSE '&' END || 
                     CASE arguments.variadic WHEN 0 THEN '' ELSE '...' END  || arguments.name || 
                     (CASE arguments.init WHEN ' ' THEN '' ELSE ' = ' || arguments.init END),
                    ', ' ) AS signature,
       cit.type AS type,
       namespaces.namespace || "\\" || lower(cit.name) AS fullnspath,
       cit.name AS class

    FROM methods
    LEFT JOIN arguments
        ON methods.id = arguments.methodId
    JOIN cit
        ON methods.citId = cit.id
    JOIN namespaces 
        ON cit.namespaceId = namespaces.id
    GROUP BY methods.id
SQL
        );

        return new Results($res);
    }

    public function fetchTableMethodsByArgument(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.type || ' ' || cit.name AS theClass, 
       namespaces.namespace || "\\" || lower(cit.name) || '::' || lower(methods.method) AS fullnspath,
       methods.method,
       arguments.name AS argument,
       init,
       typehint
FROM cit
JOIN methods 
    ON methods.citId = cit.id
JOIN arguments 
    ON methods.id = arguments.methodId AND
       arguments.citId != 0
JOIN namespaces 
    ON cit.namespaceId = namespaces.id
WHERE type in ("class", "trait", "interface")
ORDER BY fullnspath
SQL
        );

        return new Results($res);
    }

    public function fetchTableMethodsByReturnType(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.type || ' ' || cit.name AS theClass, 
       namespaces.namespace || "\\" || lower(cit.name) AS fullnspath,
       returntype, 
       methods.method
FROM cit
JOIN methods 
    ON methods.citId = cit.id
JOIN namespaces 
    ON cit.namespaceId = namespaces.id
WHERE type in ("class", "trait", "interface")
ORDER BY fullnspath
SQL
        );

        return new Results($res);
    }

    public function fetchTableClassConstants(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.name AS class, 
       classconstants.constant AS constant, 
       value, 
       namespaces.namespace || "\\" || lower(cit.name) AS fullnspath,
       visibility,
       constant,
       cit.type AS type

FROM classconstants 
JOIN cit 
    ON cit.id = classconstants.citId
JOIN namespaces 
    ON cit.namespaceId = namespaces.id

    ORDER BY cit.name, classconstants.constant, value

SQL
        );

        return new Results($res);
    }

    public function fetchTableProperty(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.name AS class, 
       namespaces.namespace || "\\" || lower(cit.name) AS fullnspath,
       visibility, 
       property, 
       value,
       cit.type AS type

    FROM cit
    JOIN properties 
        ON properties.citId = cit.id
    JOIN namespaces 
        ON cit.namespaceId = namespaces.id

SQL
        );

        return new Results($res);
    }

    public function fetchTableCit(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.*, 
       cit.type AS type, 
       namespace,

       ( SELECT GROUP_CONCAT(CASE WHEN cit5.id IS NULL THEN traits.implements ELSE cit5.name END, ',') 
       
       FROM cit_implements AS traits
LEFT JOIN cit cit5
    ON traits.implements = cit5.id
    WHERE traits.implementing = cit.id AND
       traits.type = 'use') AS use,

       (SELECT GROUP_CONCAT(CASE WHEN cit4.id IS NULL THEN implements.implements ELSE cit4.name END, ',') FROM cit_implements AS implements
LEFT JOIN cit cit4
    ON implements.implements = cit4.id
    WHERE implements.implementing = cit.id AND
       implements.type = 'implements') AS implements,

        CASE WHEN cit2.extends IS NULL THEN cit.extends ELSE cit2.name END AS extends 
        
        FROM cit

LEFT JOIN cit cit2 
    ON cit.extends = cit2.id

LEFT JOIN cit_implements AS interfaces
    ON interfaces.implementing = cit.id AND
       interfaces.type = 'implements'
LEFT JOIN cit cit4
    ON interfaces.implements = cit4.id
LEFT JOIN namespaces
    ON namespaces.id = cit.namespaceId


GROUP BY cit.id
SQL
        );

        return new Results($res);
    }

    public function fetchTablePhpcity(): Results {
        $query = <<<'SQL'
SELECT
     cit.id,
     files.file AS file,
     namespaces.namespace AS namespace,
     name,
     extends,
     (SELECT GROUP_CONCAT(implements) FROM cit_implements WHERE cit_implements.implementing = cit.id) AS implements,
     end - begin + 1 AS no_lines,
     (SELECT COUNT(*) FROM properties WHERE properties.citId = cit.id) AS no_attrs,
     (SELECT COUNT(*) FROM methods WHERE methods.citId = cit.id) AS no_methods,
     CASE type 
           WHEN 'trait' 
               THEN 1 
           ELSE 0 END AS trait,
     abstract,
     final,
     'class' AS type
        
     FROM cit
     JOIN namespaces
        ON namespaces.id = cit.namespaceId
     JOIN files
       ON cit.file = files.id
SQL;
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function fetchTableUml(): Results {
        $query = <<<'SQL'
SELECT name, cit.id, extends, type, namespace, 
       (SELECT GROUP_CONCAT(method,   "||")   FROM methods    WHERE citId = cit.id) AS methods,
       (SELECT GROUP_CONCAT( case when value != '' then property || " = " || substr(value, 0, 40) else property end, "||") FROM properties WHERE citId = cit.id) AS properties
    FROM cit
    JOIN namespaces
        ON namespaces.id = cit.namespaceId
SQL;
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function getAnalyzedFiles(array $list): int {
        $list = makeList($list);

        $query = <<<SQL
SELECT COUNT(DISTINCT results.file) 
                            FROM results 
                            JOIN files 
                                ON files.file = results.file
                            WHERE results.file != 'None'               AND 
                                  results.file LIKE '/%'               AND 
                                  analyzer IN ($list)
SQL;
        $result = $this->sqlite->querySingle($query) ?? '';

        return $result;
    }

    public function getTotalAnalyzer(): array {
        $query = <<<'SQL'
SELECT COUNT(*) AS total, 
       COUNT(CASE WHEN rc.count != 0 THEN 1 ELSE null END) AS yielding 
    FROM resultsCounts AS rc
    WHERE rc.count >= 0
SQL;
        $result = $this->sqlite->query($query);

        return $result->fetchArray(\SQLITE3_ASSOC);
    }

    public function getSeverityBreakdown(array $list): Results {
        $list = makeList($list);
        $query = <<<SQL
SELECT severity AS label, count(*) AS value
    FROM results
    WHERE analyzer IN ($list)
    GROUP BY severity
    ORDER BY value DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getFileBreakdown(array $list): Results {
        $list = makeList($list);
        $query = <<<SQL
SELECT file, count(*) AS value
    FROM results
    WHERE analyzer IN ($list)
    GROUP BY file
    ORDER BY value DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getTopAnalyzers(array $list, int $limit): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT analyzer, count(*) AS number
    FROM results
    WHERE analyzer IN ($listSQL)
    GROUP BY analyzer
    ORDER BY number DESC
    LIMIT $limit
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getSeveritiesNumberBy(array $list, string $type): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT $type, severity, count(*) AS count
    FROM results
    WHERE analyzer IN ($listSQL)
    GROUP BY $type, severity
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getAnalyzersCount(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT analyzer, count(*) AS value
    FROM results
    WHERE analyzer in ($listSQL)
    GROUP BY analyzer
    ORDER BY value DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function fetchPlantUml(): Results {
        $query = <<<SQL
SELECT name, cit.id, extends, type, namespace, 
       (SELECT GROUP_CONCAT(method,   "\n")   FROM methods    WHERE citId = cit.id) AS methods,
       (SELECT GROUP_CONCAT(visibility || ' ' || case when static != 0 then 'static ' else '' end ||  case when value != '' then property || " = " || substr(value, 0, 40) else property end, "\n") 
            FROM properties WHERE citId = cit.id) AS properties
    FROM cit
    JOIN namespaces
        ON namespaces.id = cit.namespaceId
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getFilesResultsCounts(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT file AS file, 
       line AS loc, 
       count(*) AS issues, 
       COUNT(DISTINCT analyzer) AS analyzers 
    FROM results
    WHERE line != -1 AND
          analyzer IN ($listSQL)
    GROUP BY file
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getAnalyzersResultsCounts(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT analyzer, count(*) AS issues, COUNT(DISTINCT file) AS files, 
       severity AS severity 
    FROM results
    WHERE analyzer IN ($listSQL)
    GROUP BY analyzer
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getCountFileByAnalyzers(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT count(*)  AS number
    FROM (SELECT DISTINCT file FROM results WHERE analyzer in ($listSQL))
SQL;
        $result = $this->sqlite->querySingle($query) ?? '';

        return new Results($result);
    }

    public function getFunctionsFromAnalyzer(string $analyzer): array {
        $query = <<<SQL
SELECT GROUP_CONCAT(DISTINCT REPLACE(SUBSTR(fullcode, 0, instr(fullcode, '(')), '@', ''))  AS functions FROM results 
    WHERE analyzer = "$analyzer";
SQL;
        $res = $this->sqlite->querySingle($query) ?? '';

        return explode(',', $res);
    }

    public function getCitBySize(string $type = 'class'): Results {
        $query = <<<SQL
SELECT namespaces.namespace || name AS name, 
       name AS shortName, 
       (cit.end - cit.begin + 1) AS size 
    FROM cit 
    JOIN namespaces 
        ON namespaces.id = cit.namespaceId
    WHERE
       cit.type = '$type'
    ORDER BY (cit.end - cit.begin) DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getMethodsBySize(): Results {
        $query = <<<'SQL'
SELECT namespaces.namespace || CASE namespaces.namespace WHEN '\' THEN '' ELSE '\' END || name || '::' || method AS name, 
       method AS shortName, 
       files.file, 
       (methods.end - methods.begin + 1) AS size
    FROM methods 
    JOIN cit
        on methods.citId = cit.id AND
           cit.type = 'class'
    LEFT JOIN files 
        ON files.id = cit.file
    LEFT JOIN namespaces 
        ON namespaces.id = cit.namespaceId
    ORDER BY (methods.end - methods.begin) DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getConcentratedIssues(array $list = array(), int $count = 5): Results {
        $sqlList = makeList($list);

        $query = <<<SQL
SELECT file, 
       line, 
       COUNT(*) AS count, 
       GROUP_CONCAT(DISTINCT analyzer) AS list 
    FROM results
    WHERE analyzer IN ($sqlList)
    GROUP BY file, line
    HAVING count(DISTINCT analyzer) > $count
    ORDER BY count(*) DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getIdenticalFiles(): Results {
        $query = <<<'SQL'
SELECT GROUP_CONCAT(file) AS list, 
       count(*) AS count 
    FROM files 
    GROUP BY fnv132 
    HAVING COUNT(*) > 1 
    ORDER BY COUNT(*), file
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getCitTree(string $type = 'class'): Results {
        if ($type === 'trait') {
            // Missing when raw FQN is used
            $query = <<<'SQL'
    SELECT ns.namespace || cit.name AS child, 
           ttu.implements AS parent
        FROM cit 
        JOIN
          cit_implements AS ttu 
          ON ttu.implementing = cit.id AND
             ttu.type = 'use' 
        JOIN namespaces ns
            ON cit.namespaceId = ns.id
        WHERE cit.type="trait" AND
             ttu.implements + 0 = 0
             
UNION

    SELECT ns.namespace || cit.name AS child, 
           ns2.namespace || cit2.name AS parent 
        FROM cit 
        JOIN
          cit_implements AS ttu 
          ON ttu.implementing = cit.id AND
             ttu.type = 'use'
        JOIN cit cit2 
            ON ttu.implementing = cit.id
        JOIN namespaces ns
            ON cit.namespaceId = ns.id
        JOIN namespaces ns2
            ON cit2.namespaceId = ns2.id
        WHERE cit.type="trait" AND
              cit2.type="trait"
SQL;
        } else {
            $query = <<<SQL
SELECT ns.namespace || cit.name AS child, 
       ns2.namespace || cit2.name AS parent 
    FROM cit 
    LEFT JOIN cit cit2 
        ON cit.extends = cit2.id
    JOIN namespaces ns
        ON cit.namespaceId = ns.id
    JOIN namespaces ns2
        ON cit2.namespaceId = ns2.id
    WHERE cit.type="$type" AND
          cit2.type="$type"
SQL;
        }
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getTraitConflicts(): Results {
        $query = <<<'SQL'
SELECT
   t1.name AS t1,
   t2.name AS t2,
   LOWER(SUBSTR(m1.METHOD, INSTR(m1.METHOD, 'function ') + 9, INSTR(m1.METHOD, '(') - (INSTR(m1.METHOD, 'function ') + 9))) AS method 
FROM
   cit AS t1 
   JOIN
      methods AS m1 
      ON m1.citId = t1.id 
   JOIN
      methods AS m2 
      ON m1.id != m2.id 
      AND LOWER(SUBSTR(m1.METHOD, INSTR(m1.METHOD, 'function ') + 9, INSTR(m1.METHOD, '(') - (INSTR(m1.METHOD, 'function ') + 9))) = LOWER(SUBSTR(m2.METHOD, INSTR(m2.METHOD, 'function ') + 9, INSTR(m2.METHOD, '(') - (INSTR(m2.METHOD, 'function ') + 9))) 
   JOIN
      cit AS t2 
      ON m2.citId = t2.id 
WHERE
   t1.type = 'trait' 
   AND t2.type = 'trait'
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getTraitUsage(): Results {
        $query = <<<'SQL'
SELECT
   t1.name AS t1,
   t2.name AS t2
FROM
   cit AS t1 
   JOIN
      cit_implements AS ttu 
      ON ttu.implementing = t1.id AND
         ttu.type = 'use'
   JOIN
      cit AS t2 
      ON ttu.implements = t2.id 
WHERE
   t1.type = 'trait' 
   AND t2.type = 'trait'
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

}

?><?php declare(strict_types = 1);

namespace Exakat\Dump;

use Exakat\Reports\Helpers\Results;

class Dump2 extends Dump1 {
    private const VERSION = 2;

    protected function initDump(): void {
        $query = <<<'SQL'
CREATE TABLE themas (  id    INTEGER PRIMARY KEY AUTOINCREMENT,
                       thema STRING,
                       CONSTRAINT "themas" UNIQUE (thema) ON CONFLICT IGNORE
                    )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE results (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                        fullcode STRING,
                        file STRING,
                        line INTEGER,
                        namespace STRING,
                        class STRING,
                        function STRING,
                        analyzer STRING,
                        severity STRING
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE resultsCounts ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                             analyzer STRING,
                             count INTEGER DEFAULT -6,
                             CONSTRAINT "analyzers" UNIQUE (analyzer) ON CONFLICT REPLACE
                           )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE hashAnalyzer ( id INTEGER PRIMARY KEY,
                            analyzer STRING,
                            key STRING UNIQUE,
                            value STRING
                          );
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE hashResults ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                            name STRING,
                            key STRING,
                            value STRING
                          );
SQL;
        $this->sqlite->query($query);

        // Name spaces
        $query = <<<'SQL'
CREATE TABLE namespaces (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                           namespace STRING
                        )
SQL;
        $this->sqlite->query($query);
        $this->sqlite->query("INSERT OR IGNORE INTO namespaces VALUES (1, '\\')");

        $query = <<<'SQL'
CREATE TABLE cit (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                    name STRING,
                    namespaceId INTEGER DEFAULT 1,
                    type STRING,
                    abstract INTEGER,
                    final INTEGER,
                    begin INTEGER,
                    end INTEGER,
                    file INTEGER,
                    line INTEGER,
                    extends STRING DEFAULT ""
                  )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE phpdoc (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                       type STRING,
                       type_id INTEGER,
                       phpdoc STRING
                  )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE cit_implements (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                               implementing INTEGER,
                               implements STRING,
                               type    STRING,
                               options STRING
                            )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE methods (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                        method INTEGER,
                        citId INTEGER,
                        static INTEGER,
                        final INTEGER,
                        abstract INTEGER,
                        reference INTEGER,
                        visibility STRING,
                        returntype_type STRING,
                        begin INTEGER,
                        end INTEGER
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE arguments (id INTEGER PRIMARY KEY AUTOINCREMENT,
                        name STRING,
                        citId INTEGER,
                        methodId INTEGER,
                        rank INTEGER,
                        reference INTEGER,
                        variadic INTEGER,
                        init STRING,
                        expression INTEGER,
                        hasDefault INTEGER,
                        line INTEGER,
                        typehint_type STRING
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE typehints (id INTEGER PRIMARY KEY AUTOINCREMENT,
                        type STRING,
                        object INTEGER,
                        name STRING,
                        fnp STRING
                     )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE properties (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                           property INTEGER,
                           citId INTEGER,
                           visibility STRING,
                           var INTEGER,
                           static INTEGER,
                           readonly INTEGER,
                           hasDefault INTEGER,
                           value STRING,
                           expression INTEGER,
                           line INTEGER,
                           typehint_type STRING
                           )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE classconstants ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                              constant INTEGER,
                              citId INTEGER,
                              visibility STRING,
                              value STRING
                            )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE constants (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                          constant INTEGER,
                          namespaceId INTEGER,
                          file STRING,
                          value STRING,
                          type STRING,
                          expression INTEGER
                       )
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE attributes ( id INTEGER PRIMARY KEY AUTOINCREMENT,
                          type STRING,
                          type_id INTEGER,
                          attribute STRING
)
SQL;
        $this->sqlite->query($query);

        $query = <<<'SQL'
CREATE TABLE functions (  id INTEGER PRIMARY KEY AUTOINCREMENT,
                          function STRING,
                          type STRING,
                          namespaceId INTEGER,
                          returntype_type STRING,
                          reference INTEGER,
                          file STRING,
                          begin INTEGER,
                          end INTEGER,
                          CONSTRAINT "unique" UNIQUE (function, begin)
)
SQL;
        $this->sqlite->query($query);

        $this->collectDatastore();
        $this->initTablesList();

        $time   = time();
        try {
            $id     = random_int(0, PHP_INT_MAX);
        } catch (\Throwable $e) {
            die("Couldn't generate an id for the current dump file. Aborting");
        }

        if (file_exists($this->sqliteFilePrevious)) {
            $sqliteOld = new \Sqlite3($this->sqliteFilePrevious);
            $sqliteOld->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

            $presence = $sqliteOld->querySingle('SELECT count(*) FROM sqlite_master WHERE type="table" AND name="hash"');
            if ($presence == 1) {
                $serial = $sqliteOld->querySingle('SELECT value FROM hash WHERE key="dump_serial"') + 1;
            } else {
                $serial = 0;
            }
        } else {
            $serial = 1;
        }

        $toDump = array(array('', 'dump_time',   $time),
                        array('', 'dump_id',     $id),
                        array('', 'dump_serial', $serial),
                        array('', 'dump_version', self::VERSION)
                        );

        $this->storeInTable('hash', $toDump);
        display('Inited tables');
    }

    public function fetchAnalysersCounts(array $analysers): Results {
        $query = 'SELECT analyzer, count FROM resultsCounts WHERE analyzer IN (' . makeList($analysers) . ')';
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function fetchTable(string $table, array $cols = array()): Results {
        if (empty($cols)) {
            $cols = '*';
        } else {
            $list = array();
            foreach($cols as $k => $col) {
                if (is_int($k)) {
                    $list[] = $col;
                } else {
                    $list[] = "$col as $k";
                }
            }
            $cols = implode(', ', $list);
        }

        if (!in_array($table, $this->tablesList)) {
            return new Results();
        }

        $query = "SELECT $cols FROM $table";
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function getExtensionList(): Results {
        $query = <<<'SQL'
SELECT analyzer, count(*) AS count FROM results 
    WHERE analyzer LIKE "Extensions/Ext%"
    GROUP BY analyzer
    ORDER BY count(*) DESC
SQL;

        return $this->query($query);
    }

    public function fetchHash(string $key): Results {
        $query = <<<SQL
SELECT value FROM hash WHERE key = "$key"
SQL;

        return $this->query($query);
    }

    public function fetchHashResults(string $key): Results {
        $query = <<<SQL
SELECT key, value FROM hashResults
WHERE name = "$key"
ORDER BY key + 0
SQL;

        return $this->query($query);
    }

    public function fetchHashAnalyzer(string $analyzer): Results {
        $query = <<<SQL
SELECT key, value FROM hashAnalyzer
WHERE analyzer = "$analyzer"
SQL;

        return $this->query($query);
    }

    public function getCit($type = 'class'): Results {
        assert(in_array($type, array('class', 'trait', 'interface')));

        $query = "SELECT name FROM cit WHERE type='$type' ORDER BY name";

        return $this->query($query);
    }

    private function query(string $query): Results {
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function fetchTableFunctions(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT functions.*, 
GROUP_CONCAT((CASE typehints.type WHEN ' ' THEN '' ELSE typehints.type || ' ' END ) || 
              CASE arguments.reference WHEN 0 THEN '' ELSE '&' END || 
              CASE arguments.variadic WHEN 0 THEN '' ELSE '...' END  || arguments.name || 
              (CASE arguments.init WHEN ' ' THEN '' ELSE ' = ' || arguments.init END),
             ', ' ) AS signature

FROM functions

LEFT JOIN arguments
    ON functions.id = arguments.methodId AND
       arguments.citId = 0
LEFT JOIN typehints
     ON arguments.id = typehints.object AND
        typehints.type = 'argument'
GROUP BY functions.id

SQL
        );

        return new Results($res);
    }

    public function fetchTableProperties(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT properties.*, 
       properties.property AS signature,
       cit.type AS type,
       lower(namespaces.namespace) || lower(cit.name) || '::' || lower(properties.property) AS fullnspath,
       cit.name AS class,
       cit.file AS file

    FROM properties
    JOIN cit
        ON properties.citId = cit.id
    JOIN namespaces 
        ON cit.namespaceId = namespaces.id
    GROUP BY properties.id
SQL
        );

        return new Results($res);
    }

    public function fetchTableMethods(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT methods.*, 
       GROUP_CONCAT((CASE typehints.name WHEN ' ' THEN '' ELSE typehints.name || ' ' END ) || 
                     CASE arguments.reference WHEN 0 THEN '' ELSE '&' END || 
                     CASE arguments.variadic WHEN 0 THEN '' ELSE '...' END  || arguments.name || 
                     (CASE arguments.init WHEN ' ' THEN '' ELSE ' = ' || arguments.init END),
                    ', ' ) AS signature,
       cit.type AS type,
       lower(namespaces.namespace) || lower(cit.name) || '::' || lower(methods.method) AS fullnspath,
       cit.name AS class,
       cit.file AS file,
       methods.begin AS line,
       group_concat(typehints.name) AS typehint

    FROM methods
    LEFT JOIN typehints
        ON methods.id = typehints.object
    LEFT JOIN arguments
        ON methods.id = arguments.methodId
    JOIN cit
        ON methods.citId = cit.id
    JOIN namespaces 
        ON cit.namespaceId = namespaces.id
    GROUP BY methods.id
SQL
        );

        return new Results($res);
    }

    public function fetchTableFunctionsByArgument(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT lower(namespaces.namespace) || lower(functions.function) AS fullnspath,
       functions.function,
       arguments.name AS argument,
       init,
       typehint,
       typehint_fnp,
       rank,
       arguments.line,
       files.file AS file,
       functions.begin AS line,
       type as type
FROM functions
JOIN arguments 
    ON functions.id = arguments.methodId
LEFT JOIN namespaces 
    ON functions.namespaceId = namespaces.id
JOIN files
    ON functions.file = files.id
SQL
        );

        return new Results($res);
    }

    public function fetchTableFunctionsByReturnType(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT namespaces.namespace || lower(functions.function) AS fullnspath,
       returntype, 
       type,
       functions.function,
       files.file AS file,
       functions.begin AS line
FROM functions
LEFT JOIN namespaces 
    ON functions.namespaceId = namespaces.id
JOIN files
    ON functions.file = files.id
SQL
        );

        return new Results($res);
    }

    public function fetchTableMethodsByArgument(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.type || ' ' || cit.name AS theClass, 
       cit.type AS citType,
       cit.name AS citName, 
       lower(namespaces.namespace) || lower(cit.name) || '::' || lower(methods.method) AS fullnspath,
       methods.method,
       arguments.name AS argument,
       init,
       group_concat(typehints.name) as typehint,
       group_concat(typehints.fnp) as fnp,
       rank,
       arguments.line,
       cit.file
FROM cit
LEFT JOIN methods 
    ON methods.citId = cit.id
LEFT JOIN typehints 
    ON methods.id = typehints.object
LEFT JOIN arguments 
    ON methods.id = arguments.methodId AND
       arguments.citId != 0
LEFT JOIN namespaces 
    ON cit.namespaceId = namespaces.id
WHERE cit.type in ("class", "trait", "interface", "enum")
GROUP BY methods.id
ORDER BY fullnspath
SQL
        );

        return new Results($res);
    }

    public function fetchTableMethodsByReturnType(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.type || ' ' || cit.name AS theClass, 
       namespaces.namespace || "\\" || lower(cit.name) AS fullnspath,
       group_concat(typehints.name) as returntype, 
       methods.method
FROM cit
JOIN methods 
    ON methods.citId = cit.id
JOIN typehints 
    ON methods.id = typehints.object
JOIN namespaces 
    ON cit.namespaceId = namespaces.id
WHERE cit.type in ("class", "trait", "interface")
ORDER BY fullnspath
SQL
        );

        return new Results($res);
    }

    public function fetchTableClassConstants(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.name AS class, 
       classconstants.constant AS constant, 
       value, 
       namespaces.namespace || lower(cit.name) AS fullnspath,
       visibility,
       constant,
       cit.type AS type

FROM classconstants 
JOIN cit 
    ON cit.id = classconstants.citId
JOIN namespaces 
    ON cit.namespaceId = namespaces.id

    ORDER BY cit.name, classconstants.constant, value

SQL
        );

        return new Results($res);
    }

    public function fetchTableConstants(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT constants.constant AS constant, 
       value, 
       namespaces.namespace AS namespace,
       constant

FROM constants 
JOIN namespaces 
    ON constants.namespaceId = namespaces.id

    ORDER BY namespaces.namespace, constants.constant, value

SQL
        );

        return new Results($res);
    }

    public function fetchTableProperty(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.name AS class, 
       namespaces.namespace || lower(cit.name) AS fullnspath,
       visibility, 
       property, 
       value,
       cit.type AS type

    FROM cit
    JOIN properties 
        ON properties.citId = cit.id
    JOIN namespaces 
        ON cit.namespaceId = namespaces.id

SQL
        );

        return new Results($res);
    }

    public function fetchTableCit(): Results {
        $res = $this->sqlite->query(<<<'SQL'
SELECT cit.*, 
       cit.type AS type, 
       namespace,

       ( SELECT GROUP_CONCAT(CASE WHEN cit5.id IS NULL THEN traits.implements ELSE cit5.name END, ',') 
       
       FROM cit_implements AS traits
LEFT JOIN cit cit5
    ON traits.implements = cit5.id
    WHERE traits.implementing = cit.id AND
       traits.type = 'use') AS use,

       (SELECT GROUP_CONCAT(CASE WHEN cit4.id IS NULL THEN implements.implements ELSE cit4.name END, ',') FROM cit_implements AS implements
LEFT JOIN cit cit4
    ON implements.implements = cit4.id
    WHERE implements.implementing = cit.id AND
       implements.type = 'implements') AS implements,

        CASE WHEN cit2.extends IS NULL THEN cit.extends ELSE cit2.name END AS extends 
        
        FROM cit

LEFT JOIN cit cit2 
    ON cit.extends = cit2.id

LEFT JOIN cit_implements AS interfaces
    ON interfaces.implementing = cit.id AND
       interfaces.type = 'implements'
LEFT JOIN cit cit4
    ON interfaces.implements = cit4.id
LEFT JOIN namespaces
    ON namespaces.id = cit.namespaceId


GROUP BY cit.id
SQL
        );

        return new Results($res);
    }

    public function fetchTablePhpcity(): Results {
        $query = <<<'SQL'
SELECT
     cit.id,
     files.file AS file,
     namespaces.namespace AS namespace,
     name,
     extends,
     (SELECT GROUP_CONCAT(implements) FROM cit_implements WHERE cit_implements.implementing = cit.id) AS implements,
     end - begin + 1 AS no_lines,
     (SELECT COUNT(*) FROM properties WHERE properties.citId = cit.id) AS no_attrs,
     (SELECT COUNT(*) FROM methods WHERE methods.citId = cit.id) AS no_methods,
     CASE type 
           WHEN 'trait' 
               THEN 1 
           ELSE 0 END AS trait,
     abstract,
     final,
     'class' AS type
        
     FROM cit
     JOIN namespaces
        ON namespaces.id = cit.namespaceId
     JOIN files
       ON cit.file = files.id
SQL;
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function fetchTableUml(): Results {
        $query = <<<'SQL'
SELECT name, cit.id, extends, type, namespace, 
       (SELECT GROUP_CONCAT(method,   "||")   FROM methods    WHERE citId = cit.id) AS methods,
       (SELECT GROUP_CONCAT( case when value != '' then property || " = " || substr(value, 0, 40) else property end, "||") FROM properties WHERE citId = cit.id) AS properties
    FROM cit
    JOIN namespaces
        ON namespaces.id = cit.namespaceId
SQL;
        $res = $this->sqlite->query($query);

        return new Results($res);
    }

    public function getAnalyzedFiles(array $list): int {
        $list = makeList($list);

        $query = <<<SQL
SELECT COUNT(DISTINCT results.file) 
                            FROM results 
                            JOIN files 
                                ON files.file = results.file
                            WHERE results.file != 'None'               AND 
                                  results.file LIKE '/%'               AND 
                                  analyzer IN ($list)
SQL;
        $result = $this->sqlite->querySingle($query) ?? '';

        return $result;
    }

    public function getTotalAnalyzer(): array {
        $query = <<<'SQL'
SELECT COUNT(*) AS total, 
       COUNT(CASE WHEN rc.count != 0 THEN 1 ELSE null END) AS yielding 
    FROM resultsCounts AS rc
    WHERE rc.count >= 0
SQL;
        $result = $this->sqlite->query($query);

        return $result->fetchArray(\SQLITE3_ASSOC);
    }

    public function getSeverityBreakdown(array $list): Results {
        $list = makeList($list);
        $query = <<<SQL
SELECT severity AS label, count(*) AS value
    FROM results
    WHERE analyzer IN ($list)
    GROUP BY severity
    ORDER BY value DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getFileBreakdown(array $list): Results {
        $list = makeList($list);
        $query = <<<SQL
SELECT file, count(*) AS value
    FROM results
    WHERE analyzer IN ($list)
    GROUP BY file
    ORDER BY value DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getTopAnalyzers(array $list, int $limit): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT analyzer, count(*) AS number
    FROM results
    WHERE analyzer IN ($listSQL)
    GROUP BY analyzer
    ORDER BY number DESC
    LIMIT $limit
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getSeveritiesNumberBy(array $list, string $type): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT $type, severity, count(*) AS count
    FROM results
    WHERE analyzer IN ($listSQL)
    GROUP BY $type, severity
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getAnalyzersCount(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT analyzer, count(*) AS value
    FROM results
    WHERE analyzer in ($listSQL)
    GROUP BY analyzer
    ORDER BY value DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function fetchPlantUml(): Results {
        $query = <<<SQL
SELECT name, cit.id, extends, type, namespace, 
       (SELECT GROUP_CONCAT(method,   "\n")   FROM methods    WHERE citId = cit.id) AS methods,
       (SELECT GROUP_CONCAT(visibility || ' ' || case when static != 0 then 'static ' else '' end ||  case when value != '' then property || " = " || substr(value, 0, 40) else property end, "\n") 
            FROM properties WHERE citId = cit.id) AS properties
    FROM cit
    JOIN namespaces
        ON namespaces.id = cit.namespaceId
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getFilesResultsCounts(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT file AS file, 
       line AS loc, 
       count(*) AS issues, 
       COUNT(DISTINCT analyzer) AS analyzers 
    FROM results
    WHERE line != -1 AND
          analyzer IN ($listSQL)
    GROUP BY file
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getAnalyzersResultsCounts(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT analyzer, count(*) AS issues, COUNT(DISTINCT file) AS files, 
       severity AS severity 
    FROM results
    WHERE analyzer IN ($listSQL)
    GROUP BY analyzer
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getCountFileByAnalyzers(array $list): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT count(*)  AS number
    FROM (SELECT DISTINCT file FROM results WHERE analyzer in ($listSQL))
SQL;
        $result = $this->sqlite->querySingle($query) ?? '';

        return new Results($result);
    }

    public function getFunctionsFromAnalyzer(string $analyzer): array {
        $query = <<<SQL
SELECT GROUP_CONCAT(DISTINCT REPLACE(SUBSTR(fullcode, 0, instr(fullcode, '(')), '@', ''))  AS functions FROM results 
    WHERE analyzer = "$analyzer";
SQL;
        $res = $this->sqlite->querySingle($query) ?? '';

        return explode(',', $res);
    }

    public function getCitBySize(string $type = 'class'): Results {
        $query = <<<SQL
SELECT namespaces.namespace || name AS name, 
       name AS shortName, 
       (cit.end - cit.begin + 1) AS size 
    FROM cit 
    JOIN namespaces 
        ON namespaces.id = cit.namespaceId
    WHERE
       cit.type = '$type'
    ORDER BY (cit.end - cit.begin) DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getMethodsBySize(): Results {
        $query = <<<SQL
SELECT namespaces.namespace || CASE namespaces.namespace WHEN '\' THEN '' ELSE '\' END || name || '::' || method AS name, 
       method AS shortName, 
       files.file, 
       (methods.end - methods.begin + 1) AS size
    FROM methods 
    JOIN cit
        on methods.citId = cit.id AND
           cit.type = 'class'
    LEFT JOIN files 
        ON files.id = cit.file
    LEFT JOIN namespaces 
        ON namespaces.id = cit.namespaceId
    ORDER BY (methods.end - methods.begin) DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getConcentratedIssues(array $list = array(), int $count = 5): Results {
        $sqlList = makeList($list);

        $query = <<<SQL
SELECT file, 
       line, 
       COUNT(*) AS count, 
       GROUP_CONCAT(DISTINCT analyzer) AS list 
    FROM results
    WHERE analyzer IN ($sqlList)
    GROUP BY file, line
    HAVING count(DISTINCT analyzer) > $count
    ORDER BY count(*) DESC
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getIdenticalFiles(): Results {
        $query = <<<'SQL'
SELECT GROUP_CONCAT(file) AS list, 
       count(*) AS count 
    FROM files 
    GROUP BY fnv132 
    HAVING COUNT(*) > 1 
    ORDER BY COUNT(*), file
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getCitTree(string $type = 'class'): Results {
        if ($type === 'trait') {
            // Missing when raw FQN is used
            $query = <<<'SQL'
    SELECT ns.namespace || cit.name AS child, 
           ttu.implements AS parent
        FROM cit 
        JOIN
          cit_implements AS ttu 
          ON ttu.implementing = cit.id AND
             ttu.type = 'use' 
        JOIN namespaces ns
            ON cit.namespaceId = ns.id
        WHERE cit.type="trait" AND
             ttu.implements + 0 = 0
             
UNION

    SELECT ns.namespace || cit.name AS child, 
           ns2.namespace || cit2.name AS parent 
        FROM cit 
        JOIN
          cit_implements AS ttu 
          ON ttu.implementing = cit.id AND
             ttu.type = 'use'
        JOIN cit cit2 
            ON ttu.implementing = cit.id
        JOIN namespaces ns
            ON cit.namespaceId = ns.id
        JOIN namespaces ns2
            ON cit2.namespaceId = ns2.id
        WHERE cit.type="trait" AND
              cit2.type="trait"
SQL;
        } else {
            $query = <<<SQL
SELECT ns.namespace || cit.name AS child, 
       ns2.namespace || cit2.name AS parent 
    FROM cit 
    LEFT JOIN cit cit2 
        ON cit.extends = cit2.id
    JOIN namespaces ns
        ON cit.namespaceId = ns.id
    JOIN namespaces ns2
        ON cit2.namespaceId = ns2.id
    WHERE cit.type="$type" AND
          cit2.type="$type"
SQL;
        }
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getTraitConflicts(): Results {
        $query = <<<'SQL'
SELECT
   t1.name AS t1,
   t2.name AS t2,
   LOWER(SUBSTR(m1.METHOD, INSTR(m1.METHOD, 'function ') + 9, INSTR(m1.METHOD, '(') - (INSTR(m1.METHOD, 'function ') + 9))) AS method 
FROM
   cit AS t1 
   JOIN
      methods AS m1 
      ON m1.citId = t1.id 
   JOIN
      methods AS m2 
      ON m1.id != m2.id 
      AND LOWER(SUBSTR(m1.METHOD, INSTR(m1.METHOD, 'function ') + 9, INSTR(m1.METHOD, '(') - (INSTR(m1.METHOD, 'function ') + 9))) = LOWER(SUBSTR(m2.METHOD, INSTR(m2.METHOD, 'function ') + 9, INSTR(m2.METHOD, '(') - (INSTR(m2.METHOD, 'function ') + 9))) 
   JOIN
      cit AS t2 
      ON m2.citId = t2.id 
WHERE
   t1.type = 'trait' 
   AND t2.type = 'trait'
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getTraitUsage(): Results {
        $query = <<<'SQL'
SELECT
   t1.name AS t1,
   t2.name AS t2
FROM
   cit AS t1 
   JOIN
      cit_implements AS ttu 
      ON ttu.implementing = t1.id AND
         ttu.type = 'use'
   JOIN
      cit AS t2 
      ON ttu.implements = t2.id 
WHERE
   t1.type = 'trait' 
   AND t2.type = 'trait'
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getClassCounts(): Results {
        $query = <<<'SQL'
SELECT
   cit.name AS cit,
   cit.id AS id,
   cit.extends AS extends,
   ns.namespace AS namespace,
   count(distinct m.id) AS methods,
   count(distinct p.id) AS properties,
   count(distinct c.id) AS constants
   
FROM
   cit 
   LEFT JOIN
      cit_implements AS ttu 
      ON ttu.implementing = cit.id AND
         ttu.type = 'use'
   JOIN namespaces ns
        ON cit.namespaceId = ns.id
   LEFT JOIN
      methods AS m 
      ON cit.id = m.citId
   LEFT JOIN
      properties AS p 
      ON cit.id = p.citId
   LEFT JOIN
      classConstants AS c
      ON cit.id = c.citId
WHERE
   cit.type = 'class' 
GROUP BY cit.id

SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getFilesWithResults(array $list = array()): Results {
        $listSQL = makeList($list);

        $query = <<<SQL
SELECT
    DISTINCT file
FROM
   results 
WHERE analyzer IN ($listSQL)
SQL;
        $result = $this->sqlite->query($query);

        return new Results($result);
    }

    public function getExtendedDependencies(): Results {
        $query = <<<'SQL'
SELECT extending,  namespaces.namespace || cit.name AS origin
    FROM dependenciesExtensions
    LEFT JOIN cit
    LEFT JOIN namespaces 
        on cit.namespaceId = namespaces.id 
    WHERE LOWER(namespaces.namespace || cit.name ) = origin

SQL;
        $result = $this->sqlite->query($query);
        return new Results($result);
    }
}



?><?php declare(strict_types = 1);

namespace Exakat\Dump;

use Sqlite3;
use Exakat\Reports\Helpers\Results;

abstract class Dump {
    public const READ  = 1;
    public const INIT  = 0;

    protected $project          = null;
    protected $phpexcutable     = null;
    protected $sqlite           = null;
    protected $sqliteFileFinal    = '';
    protected $sqliteFile         = null;
    protected $sqliteFilePrevious = null;

    protected $tablesList = array();

    public function __construct(string $path, int $init = self::READ, string $project = '', string $phpexecutable = '') {
        $this->sqliteFileFinal    = $path;
        $this->sqliteFile         = str_replace('/dump', '/.dump', $this->sqliteFileFinal);
        $this->sqliteFilePrevious = str_replace('/dump', '/dump-1', $this->sqliteFileFinal);

        $this->project        = $project;
        $this->phpexcutable   = $phpexecutable;

        if ($init === self::INIT) {
            if (file_exists($this->sqliteFile)) {
                unlink($this->sqliteFile);
                display('Removing old .dump.sqlite');
            }

            if (file_exists($this->sqliteFileFinal)) {
                $this->reuse();
            } else {
                $this->init();
            }
        } else {
            $this->openForRead();
        }
    }

    private function reuse(): void {
        copy($this->sqliteFileFinal, $this->sqliteFile);
        $this->sqlite = new \Sqlite3($this->sqliteFile, \SQLITE3_OPEN_READWRITE);
        $this->sqlite->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

        $this->initTablesList();
    }

    private function init(): void {
        $this->sqlite = new Sqlite3($this->sqliteFile, \SQLITE3_OPEN_READWRITE | \SQLITE3_OPEN_CREATE);
        $this->sqlite->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

        $this->initDump();
    }

    private function openForRead(): void {
        if (file_exists($this->sqliteFile)) {
            unlink($this->sqliteFile);
            display('Removing old .dump.sqlite');
        }

        $this->sqlite = new \Sqlite3($this->sqliteFileFinal,  \SQLITE3_OPEN_READONLY);
        $this->sqlite->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

        $this->initTablesList();
    }

    protected function initTablesList(): void {
        $res = $this->sqlite->query("SELECT name FROM sqlite_master WHERE type ='table' AND name NOT LIKE 'sqlite_%'");
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $this->tablesList[] = $row['name'];
        }
    }

    public static function factory(string $path, int $init = self::READ): self {
        return new Dump2($path, $init);
    }

    protected function collectDatastore(): void {
        $tables = array(//'analyzed',
                        'compilation52',
                        'compilation53',
                        'compilation54',
                        'compilation55',
                        'compilation56',
                        'compilation70',
                        'compilation71',
                        'compilation72',
                        'compilation73',
                        'compilation74',
                        'compilation80',
                        'compilation81',
                        'compilation82',
                        'composer',
                        'configFiles',
                        'externallibraries',
                        'files',
                        'hash',
                        'ignoredFiles',
                        'shortopentag',
                        'tokenCounts',
                        'linediff',
                        );
        $this->collectTables($tables);
    }

    public function removeResults(array $analyzers): void {
        $classesList = makeList($analyzers);

        $this->sqlite->query("DELETE FROM results WHERE analyzer IN ($classesList)");
        $this->sqlite->query("DELETE FROM resultsCounts WHERE analyzer IN ($classesList)");
    }

    public function addResults(array $toDump): array {
        if (empty($toDump)) {
            return array();
        }

        $chunks = array_chunk($toDump, SQLITE_CHUNK_SIZE);
        foreach($chunks as $chunk) {
            foreach($chunk as &$c) {
                assert(count($c) === 8, 'Wrong column count for results : ' . print_r($c, true));
                $c = array_map(array($this->sqlite, 'escapeString'), $c);
                $c = '(NULL, \'' . implode('\', \'', $c) . '\')';
            }
            unset($c);

            $sql = 'REPLACE INTO results ("id", "fullcode", "file", "line", "namespace", "class", "function", "analyzer", "severity") VALUES ' . implode(', ', $chunk);
            $this->sqlite->query($sql);
        }

        $return = array_column($toDump, 6);
        $return = array_count_values($return);

        $query = array();
        foreach($return as $k => $v) {
            $query[] = "(NULL, '$k', $v)";
        }

        $this->sqlite->query('INSERT INTO resultsCounts ("id", "analyzer", "count") VALUES ' . implode(', ', $query));

        // Pretty sneaaaaky, as it doesn't count the stored rows
        return $return;
    }

    public function addEmptyResults(array $toDump): void {
        $chunks = array_chunk($toDump, SQLITE_CHUNK_SIZE);
        foreach($chunks as $chunk) {
            foreach($chunk as &$c) {
                $c = "(NULL, '" . $c . "', 0)";
            }
            unset($c);
            $sql = 'REPLACE INTO resultsCounts ("id", "analyzer", "count") VALUES ' . implode(', ', $chunk);
            $this->sqlite->query($sql);
        }
    }

    public function getTableCount(string $table): int {
        return $this->sqlite->querySingle('SELECT count(*) FROM ' . $table);
    }

    public function collectTables(array $tables): void {
        $config = exakat('config');
        $this->sqlite->query("ATTACH '{$config->datastore}' AS datastore");

        $query = "SELECT name, sql FROM datastore.sqlite_master WHERE type='table' AND name in ('" . implode("', '", $tables) . "');";
        $existingTables = $this->sqlite->query($query);

        while($table = $existingTables->fetchArray(\SQLITE3_ASSOC)) {
            $createTable = $table['sql'];
            $createTable = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ', $createTable);

            $this->sqlite->query($createTable);
            $this->sqlite->query('REPLACE INTO ' . $table['name'] . ' SELECT * FROM datastore.' . $table['name']);
        }

        $this->sqlite->query('DETACH datastore');
    }

    public function close(): void {
        $this->sqlite->query('REPLACE INTO resultsCounts ("id", "analyzer", "count") VALUES (NULL, \'Project/Dump\', 1)');

        rename($this->sqliteFile, $this->sqliteFileFinal);
    }

    public function cleanTable(string $table): void {
        $query = 'DELETE FROM ' . $table;
        $this->sqlite->query($query);
    }

    public function storeInTable(string $table, Iterable $results): int {
        $values = array();
        $total  = 0;
        foreach($results as $change) {
            $first = array_shift($change);
            $values[] = '(' . (empty($first) ? 'null' : $first) . ',' . makeList(array_map(array($this->sqlite, 'escapeString'), $change), "'" ) . ')';
            // str_replace is an ugly hack for id, which should be null.
            ++$total;
        }

        if (!empty($values)) {
            $chunks = array_chunk($values, SQLITE_CHUNK_SIZE);
            foreach($chunks as $chunk) {
                $query = 'REPLACE INTO ' . $table . ' VALUES ' . implode(', ', $chunk);
                $this->sqlite->query($query);
            }
        }

        return count($values);
    }

    public function storeQueries(array $queries): int {
        $this->sqlite->lastErrorCode();
        foreach($queries as $query) {
            $res = $this->sqlite->query($query);
            if ($this->sqlite->lastErrorCode()) {
                print  $query . PHP_EOL . PHP_EOL;
            }
        }

        return count($queries);
    }

    public function fetchHashResults(string $key): Results {
        return new Results();
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoStructureForTable extends \RuntimeException {
    public function __construct(string $table) {

        parent::__construct( "No SQL structure exists for table $table.\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class AnotherProcessIsRunning extends \RuntimeException {
    public function __construct(int $code = 0, Exception $previous = null) {

        parent::__construct( "Another exakat is running. Wait until it finishes to launch this command again.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class NoDump extends \Exception {
    public function __construct(string $project = '', int $code = 0, Exception $previous = null) {

        parent::__construct("No results database was found for project '$project'.\nRun php exakat.phar project -p $project first\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class NoSuchLoader extends \RuntimeException {
    public function __construct(string $loader = '', array $loaderList = array(),int $code = 0, Exception $previous = null) {
        parent::__construct('No such loader as "' . $loader . '". Use one of ' . implode(', ', $loaderList) . "\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoPhpBinary extends \Exception {
    public function __construct($message = '', $code = 0, \Exception $previous = null) {
        parent::__construct($message, $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class ProjectNotInited extends \RuntimeException {
    public function __construct($project = '', $code = 0, ?Exception $previous = null) {
        parent::__construct("Project $project hasn't been initialized. Run exakat init first.", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoSuchFile extends \RuntimeException {
    public function __construct($filename = '', $code = 0, \Exception $previous = null) {
        parent::__construct('No such file as "' . $filename . "\"\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class DSLException extends \Exception {
    public function __construct(string $message, int $level = 1) {
        parent::__construct($message);

        // Setting error in the previous file
        $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
        $this->file = $trace[$level]['file'];
        $this->line = $trace[$level]['line'];
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class ProjectNeeded extends \RuntimeException {
    public function __construct() {

        parent::__construct( "This command requires a project name. Pass the -p option, or use .exakat.yaml config file.\nAborting\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class ProjectTooLarge extends \RuntimeException {
    public function __construct($nb_tokens, $limit) {
        parent::__construct("Project too large ($nb_tokens tokens found, $limit tokens limit). Check config/exakat.ini to change this limit.\n", 0, null);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class InvalidProjectName extends \Exception {
    public function __construct($message = '', $code = 0, $previous = null) {

        parent::__construct("Can't use a project with that name. $message\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class VcsSupport extends \Exception {
    public function __construct(string $vcs, string $message = 'doesn\'t support this feature.', int $code = 0, ?Exception $previous = null) {

        parent::__construct("$vcs $message.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class WrongNumberOfColsForAHash extends \RuntimeException {
    public function __construct(string $table, int $provided) {

        parent::__construct( "Wrong number of cols provided for table $table : $provided cols provided.\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class NoRecognizedTokens extends \Exception {
    public function __construct(string $message = '', int $code = 0, ?Exception $previous = null) {
        parent::__construct('Tokens \'' . $message . '\' were not recognized. Please, check with @exakat on twitter.', $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class UnknownDsl extends \RuntimeException {
    public function __construct(string $name) {
        parent::__construct( "Unknown DSL command '$name'\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoSuchDir extends \RuntimeException {
    public function __construct($filename = '', $code = 0, \Exception $previous = null) {
        parent::__construct('No such dir as "' . $filename . '"', $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoSuchProject extends \RuntimeException {
    public function __construct($project = '', $code = 0, \Exception $previous = null) {
        parent::__construct("No such project as \"$project\".\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class InvalidPHPBinary extends \Exception {
    public function __construct(string $message = '', int $code = 0, Exception $previous = null) {

        parent::__construct("This PHP version ($message) is not valid for running Exakat. You need PHP 7.0 or later.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NeedsAnalyzerThema extends \RuntimeException {
    public function __construct() {

        parent::__construct( "One of the options -P <One/rule>|-T <\"Thema\"> is required. None provided.\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class NoDumpYet extends \Exception {
    public function __construct(string $project = '', int $code = 0, Exception $previous = null) {

        parent::__construct("No results are available for project '$project' yet. May be the analysis is still running.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class InaptPHPBinary extends \Exception {
    public function __construct(string $message = '', int $code = 0, Exception $previous = null) {

        parent::__construct("The PHP version cannot be used to run Exakat : $message\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class GremlinException extends \Exception {
    public function __construct(string $message = '', string $query = '', Exception $previous = null) {

        parent::__construct("Error during Gremlin query : '$message'.\nQuery : $query\n", 1, $previous);
    }
}

?><?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

declare(strict_types = 1);

namespace Exakat\Exceptions;

class WrongNumberOfArguments extends \RuntimeException {
    public function __construct(string $method, int $obtained, int $expected) {
        parent::__construct("$method received $obtained arguments, and expected $expected.", 0, null);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class QueryException extends \RuntimeException {
    public function __construct($message, $code = 0, \Exception $previous = null) {

        parent::__construct( "Gremlin Query Exception : $message\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class LoadError extends \Exception {
    public function __construct(string $message, int $code = 0, Exception $previous = null) {

        parent::__construct("Load task met an error : '$message'\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NotProjectInGraph extends \RuntimeException {
    public function __construct($project = '', $inGraph = '') {
        parent::__construct('The analyzed project is not the same as the requested one : "' . $project . '" / "' . $inGraph . '". You probably want to use  -p ' . $inGraph . '.', 0, null);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class MustBeADir extends \Exception {
    public function __construct($message = '', $code = 0, \Exception $previous = null) {

        parent::__construct("$message must be a directory, or use -f option. \nAborting.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class HelperException extends \Exception {
    public function __construct($helper = '', $code = 0, \Exception $previous = null) {

        parent::__construct($helper . ' not found. Please, check exakat doctor to ensure all helpers are available', $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class UnknownGremlinVersion extends \RuntimeException {
    public function __construct(string $version) {
        parent::__construct( "Unknown Gremlin version installed : '$version'\n");
    }
}

?><?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

declare(strict_types = 1);

namespace Exakat\Exceptions;

class WrongParameterType extends \RuntimeException {
    public function __construct(string $vcs = '', string $message = '') {
        parent::__construct("$vcs reported an error and no code could be loaded : $message.", 0, null);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class NoFileToProcess extends \RuntimeException {
    public function __construct(string $filename = '', string $type = 'empty or doesn\'t compile', int $code = 0, Exception $previous = null) {
        parent::__construct($filename . ' ' . $type, $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoSuchRuleset extends \RuntimeException {
    public function __construct(string $message = '',array $rulesets = array()) {
        $exception = "No such ruleset as '$message'. \n";

        if (!empty($rulesets)) {
            $exception .= 'You can try : ' . implode(', ', array_unique($rulesets)) . PHP_EOL;
        }

        parent::__construct($exception);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class UnknownCase extends \RuntimeException {
    public function __construct(string $message) {

        parent::__construct("An unexpected situation was met while processing Load : $message\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoCodeInProject extends \RuntimeException {
    public function __construct($project = '', $code = 0, \Exception $previous = null) {
        parent::__construct("No code in project '$project'\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Exceptions;

class VcsError extends \RuntimeException {
    public function __construct(string $vcs = '', string $message = '') {
        parent::__construct("$vcs reported an error and no code could be loaded : $message.", 0, null);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class MustBeAFile extends \Exception {
    public function __construct($message = '', $code = 0, \Exception $previous = null) {

        parent::__construct("$message must be a file, or use -d option. \nAborting.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;


class NoSuchAnalyzer extends \RuntimeException {
    public function __construct($analyzer, $themes) {
        $die = "Couldn't find '$analyzer'. Aborting\n";

        if (preg_match('#[a-z0-9_]+/[a-z0-9_]+$#i', $analyzer) === 0) {
            $die .= "Analyzers use the format Folder/Rule, for example Structures/UselessInstructions. Check the documentation http://exakat.readthedocs.io/\n";
        } else {
            $r = $themes->getSuggestionClass($analyzer);
            if (empty($r)) {
                $die .= "Couldn't find a suggestion. Check the documentation http://exakat.readthedocs.io/\n";
            } else {
                $die .= 'Did you mean : ' . str_replace('\\', '/', implode(', ', array_slice($r, 0, 5)));
                if (count($r) > 5) {
                    $die .= ' (' . (count($r) - 5) . ' more possible)';
                }
                $die .= "\n";
            }
        }

        parent::__construct($die);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

use Exception;

class NoPrecedence extends \Exception {
    public function __construct($message = '', $code = 0, Exception $previous = null) {

        parent::__construct("No precedence for '$message'.\n", $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoSuchReport extends \RuntimeException {
    public function __construct(string $report = '') {
        parent::__construct("No such report as '$report'\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class MissingFile extends \Exception {
    public function __construct(array $missing = array(), int $code = 0, \Exception $previous = null) {

        $c = count($missing);
        if ($c > 3) {
            $display = array_slice($missing, 0, 3);
            $displayNames = implode(', ', $display) . '...';
        } else {
            $displayNames = implode(', ', $missing);
        }
        parent::__construct($c . ' file' . ($c > 1 ? 's are' : ' is') . ' missing from the cache (' . $displayNames . '). Please, run "files" command to refresh the cache. ' . PHP_EOL . 'Aborting' . PHP_EOL, $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class MissingGremlin extends \RuntimeException {
    public function __construct(string $gremlin = '') {
        parent::__construct("Gremlin server could not be found in '$gremlin'. Check config/exakat.ini.\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoJobqueueStarted extends \RuntimeException {
    public function __construct() {

        parent::__construct( 'No Jobqueue server was found on this server. Start the queue (exakat queue) and try again.');
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Exceptions;

class NoSuchFormat extends \RuntimeException {
    public function __construct($requested, $formats, $code = 0, \Exception $previous = null) {
        parent::__construct("Format '" . $requested . "' doesn't exist. Choose among : " . implode(', ', $formats), $code, $previous);
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph;

use Exakat\Graph\Helpers\GraphResults;
use Exakat\Exceptions\GremlinException;
use Exakat\Exceptions\UnknownGremlinVersion;
use Brightzone\GremlinDriver\Connection;
use Exakat\Helpers\Timer;

class Tinkergraph extends Graph {
    public const CHECKED = true;
    public const UNCHECKED = false;
    public const UNAVAILABLE = 1;

    private $status     = self::UNCHECKED;
    private $db         = null;

    private $gremlinVersion = '3.4';

    public function init(): void {
        if (!file_exists("{$this->config->tinkergraph_folder}/lib/")) {
            // No local production, just skip init.
            $this->status = self::UNAVAILABLE;
            return;
        }

        $gremlinJar = glob("{$this->config->tinkergraph_folder}/lib/gremlin-core-*.jar");
        $gremlinVersion = basename(array_pop($gremlinJar) ?? '');

        $this->gremlinVersion = substr($gremlinVersion, 13, -6);
        if (in_array($this->gremlinVersion, array('3.4'), STRICT_COMPARISON)) {
            throw new UnknownGremlinVersion($this->gremlinVersion);
        }

        $this->db = new Connection(array( 'host'  => $this->config->tinkergraph_host,
                                          'port'  => $this->config->tinkergraph_port,
                                          'graph' => 'graph',
                                          'emptySet' => true,
                                   ) );
        $this->status = self::UNCHECKED;
    }

    private function checkConfiguration(): void {
        ini_set('default_socket_timeout', '1600');
        $this->db->open();
    }

    public function getInfo(): array {
        $stats = array();

        if (empty($this->config->tinkergraphv3_folder)) {
            $stats['configured'] = 'No tinkergraph configured in config/exakat.ini.';
        } elseif (!file_exists($this->config->tinkergraphv3_folder)) {
            $stats['installed'] = 'No (folder : ' . $this->config->tinkergraphv3_folder . ')';
        } else {
            $stats['installed'] = 'Yes (folder : ' . $this->config->tinkergraphv3_folder . ')';
            $stats['host'] = $this->config->tinkergraph_host;
            $stats['port'] = $this->config->tinkergraph_port;

            $gremlinJar = glob("{$this->config->tinkergraphv3_folder}/lib/gremlin-core-*.jar");
            $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
            //example : gremlin-core-3.2.5.jar
            $gremlinVersion = substr($gremlinVersion, 13, -4);

            $stats['gremlin version'] = $gremlinVersion;

            if (file_exists("{$this->config->tinkergraph_port}/db/tinkergraph.pid")) {
                $stats['running'] = 'Yes (PID : ' . trim(file_get_contents("{$this->config->tinkergraph_port}/db/tinkergraph.pid")) . ')';
            }
        }

        return $stats;
    }

    public function query(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNAVAILABLE) {
            return new GraphResults();
        } elseif ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $params['#jsr223.groovy.engine.keep.globals'] = 'phantom';
        foreach ($params as $name => $value) {
            $this->db->message->bindValue($name, $value);
        }

        $result = $this->db->send($query);

        if (empty($result)) {
            return new GraphResults();
        } elseif ($result[0] === null) {
            return new GraphResults();
        } elseif (is_array($result[0])) {
            if (isset($result[0]['type'])) {
                $result = $this->simplifyArray($result);
            }

            return new GraphResults($result);
        } elseif (is_array($result)) {
            return new GraphResults($result);
        } elseif ($result instanceof \stdClass) {
            return new GraphResults($result);
        } else {
            print 'Processing unknown type ' . gettype($result) . PHP_EOL;
            var_dump($result);
            die();
        }
    }

    public function queryOne(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        return $this->query($query, $params, $load);
    }

    public function checkConnection(): bool {
        $res = @stream_socket_client('tcp://' . $this->config->tinkergraph_host . ':' . $this->config->tinkergraph_port,
                                     $errno,
                                     $errorMessage,
                                     1,
                                     STREAM_CLIENT_CONNECT
                                     );

        return is_resource($res);
    }

    public function serverInfo(): array {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $res = $this->query('Gremlin.version();');

        return $res->toArray();
    }

    public function clean(): void {
        // This is memory only Database
        $this->stop();
        $this->start();
    }

    public function start(): void {
        if (!file_exists("{$this->config->tinkergraph_folder}/conf")) {
            throw new GremlinException('No tinkgergraph configuration folder found.');
        }

        if (!file_exists("{$this->config->tinkergraph_folder}/conf/tinkergraph.{$this->gremlinVersion}.yaml")) {
            copy("{$this->config->dir_root}/server/tinkergraph/tinkergraph.{$this->gremlinVersion}.yaml",
                 "{$this->config->tinkergraph_folder}/conf/tinkergraph.{$this->gremlinVersion}.yaml");
        }

        if (in_array($this->gremlinVersion, array('3.3', '3.4'), STRICT_COMPARISON)) {
            putenv("GREMLIN_YAML=conf/tinkergraph.{$this->gremlinVersion}.yaml");
            putenv('PID_DIR=db');
            exec("GREMLIN_YAML=conf/tinkergraph.{$this->gremlinVersion}.yaml; PID_DIR=db; cd {$this->config->tinkergraph_folder}; rm -rf db/neo4j;/bin/bash ./bin/gremlin-server.sh start > gremlin.log 2>&1 &");
        } elseif ($this->gremlinVersion === '3.2') {
            exec("cd {$this->config->tinkergraph_folder}; rm -rf db/neo4j;/bin/bash ./bin/gremlin-server.sh conf/tinkergraph.yaml  > gremlin.log 2>&1 & echo $! > db/tinkergraph.{$this->gremlinVersion}.pid ");
        } else {
            throw new GremlinException("Wrong version for tinkergraph : $this->gremlinVersion");
        }
        $this->init();
        sleep(1);

        $timer = new Timer();
        $round = -1;
        do {
            $res = $this->checkConnection();
            ++$round;
            usleep(100000 * $round);
        } while (!$res && $round < 20);
        $timer->end();

        display("Restarted in $round rounds\n");

        if (file_exists("{$this->config->tinkergraph_folder}/run/gremlin.pid")) {
            $pid = trim(file_get_contents("{$this->config->tinkergraph_folder}/run/gremlin.pid"));
        } elseif (file_exists("{$this->config->tinkergraph_folder}/db/tinkergraph.pid")) {
            $pid = trim(file_get_contents("{$this->config->tinkergraph_folder}/db/tinkergraph.pid"));
        } else {
            $pid = 'Not yet';
        }

        display('started [' . $pid . '] in ' . number_format($timer->duration(Timer::MS), 2) . ' ms' );
    }

    public function stop(): void {
        if (file_exists("{$this->config->tinkergraph_folder}/db/gremlin.pid")) {
            display("stop gremlin server {$this->gremlinVersion}");
            putenv("GREMLIN_YAML=conf/tinkergraph.{$this->gremlinVersion}.yaml");
            putenv('PID_DIR=db');
            shell_exec("cd {$this->config->tinkergraph_folder}; ./bin/gremlin-server.sh stop");
            if (file_exists("{$this->config->tinkergraph_folder}/db/gremlin.pid")) {
                unlink("{$this->config->tinkergraph_folder}/db/gremlin.pid");
            }
        }

        if (file_exists("{$this->config->tinkergraph_folder}/db/tinkergraph.pid")) {
            display('stop gremlin server 3.2');
            shell_exec("kill -9 $(cat {$this->config->tinkergraph_folder}/db/tinkergraph.pid) 2>> gremlin.log; rm -f {$this->config->tinkergraph_folder}/db/tinkergraph.pid");
        }
    }

    private function simplifyArray(array $result): array {
        $return = array();

        if (!isset($result[0]['properties'])) {
            return $result;
        }

        foreach ($result as $r) {
            $row = array('id'    => $r['id'],
                         'label' => $r['label']);
            foreach ($r['properties'] as $property => $value) {
                $row[$property] = $value[0]['value'];
            }

            $return[] = $row;
        }

        return $return;
    }

    public function getDefinitionSQL(): string {
        // Optimize loading by sorting the results
        return <<<'SQL'
SELECT DISTINCT CASE WHEN definitions.id IS NULL THEN definitions2.id ELSE definitions.id END AS definition, GROUP_CONCAT(DISTINCT calls.id) AS call, count(calls.id) AS id
FROM calls
LEFT JOIN definitions 
    ON definitions.type       = calls.type       AND
       definitions.fullnspath = calls.fullnspath
LEFT JOIN definitions definitions2
    ON definitions2.type       = calls.type       AND
       definitions2.fullnspath = calls.globalpath 
WHERE (definitions.id IS NOT NULL OR definitions2.id IS NOT NULL)
GROUP BY definition
SQL;
    }

    public function getGlobalsSql(): string {
        return 'SELECT origin, destination FROM globals';
    }
}

?>
   Bud1           	                                                           e r sbwspbl                                                                                                                                                                                                                                                                                                                                                                                                                                           H e l p e r sbwspblob   bplist00	
			]ShowStatusBar_SidebarWidthTenElevenOrLater[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar[ShowPathbar	#@j     		_{{-834, 141}, {770, 436}}	'FR^u                                H e l p e r svSrnlong                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   E  	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       DSDB                                 `                                                   @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              <?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph;

use Exakat\Graph\Helpers\GraphResults;

class NoGremlin extends Graph {
    public function query(string $query, array $params = array(),array $load = array()): GraphResults {
        return new GraphResults();
    }

    public function queryOne(string $query, array $params = array(),array $load = array()): GraphResults {
        return new GraphResults();
    }

    public function init(): void {
    }

    public function start(): void {
    }

    public function stop(): void {
    }

    public function serverInfo(): array {
        return array('Server' => 'None');
    }

    public function checkConnection(): bool {
        return true;
    }

    public function clean(): void {
    }

    public function getDefinitionSQL(): string {
        return 'PRAGMA no_sql;';
    }

    public function getInfo(): array {
        return array('installed' => 'Always',
                    );
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph;

use Exakat\Graph\Helpers\GraphResults;
use Exakat\Exceptions\GremlinException;
use Exakat\Exceptions\UnknownGremlinVersion;
use Brightzone\GremlinDriver\Connection;
use Exakat\Helpers\Timer;

class GSNeo4jV3 extends Graph {
    public const CHECKED     = true;
    public const UNCHECKED   = false;
    public const UNAVAILABLE = 2;

    public const GREMLIN_VERSIONS = array('3.4', '3.5');
    private $gremlinVersion = '3.4';

    private $status     = self::UNCHECKED;

    private $db         = null;

    public function getInfo(): array {
        $stats = array();

        if (empty($this->config->gsneo4jv3_folder)) {
            $stats['configured'] = 'No gsneo4jv3_folder configured in config/exakat.ini.';
        } elseif (!file_exists($this->config->gsneo4jv3_folder)) {
            $stats['installed'] = 'No (folder : ' . $this->config->gsneo4jv3_folder . ')';
        } elseif (!file_exists($this->config->gsneo4jv3_folder . '/ext/neo4j-gremlin/')) {
            $stats['installed'] = 'Partially (missing neo4j folder : ' . $this->config->gsneo4jv3_folder . ')';
        } else {
            $stats['installed'] = "Yes (folder : {$this->config->gsneo4jv3_folder})";
            $stats['host'] = $this->config->gsneo4jv3_host;
            $stats['port'] = $this->config->gsneo4jv3_port;

            $plugins = glob("{$this->config->gsneo4jv3_folder}/ext/neo4j-gremlin/plugin/*.jar");
            if (count($plugins) !== 72) {
                $stats['grapes failed'] = 'Partially installed neo4j plugin. Please, check installation docs, and "grab" again : some of the files are missing for neo4j.';
            }

            $gremlinJar = glob("{$this->config->gsneo4jv3_folder}/lib/gremlin-core-*.jar");
            $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
            //gremlin-core-3.4.10.jar
            $gremlinVersion = substr($gremlinVersion, 13, -4);
            $stats['gremlin version'] = $gremlinVersion;

            $neo4jJar = glob("{$this->config->gsneo4jv3_folder}/ext/neo4j-gremlin/lib/neo4j-*.jar");
            $neo4jJar = array_filter($neo4jJar, function ($x) { return preg_match('#/neo4j-\d\.\d\.\d\.jar#', $x); });
            $neo4jVersion = basename(array_pop($neo4jJar) ?? '');

            //neo4j-2.3.3.jar
            $neo4jVersion = substr($neo4jVersion, 6, -4);
            $stats['neo4j version'] = $neo4jVersion;

            if (file_exists("{$this->config->gsneo4jv3_folder}/db/gsneo4jv3.pid")) {
                $stats['running'] = 'Yes (PID : ' . trim(file_get_contents("{$this->config->gsneo4jv3_folder}/db/gsneo4jv3.pid")) . ')';
            }
        }

        return $stats;
    }

    public function init(): void {
        if (!file_exists("{$this->config->gsneo4jv3_folder}/lib/")) {
            // No local production, just skip init.
            $this->status = self::UNAVAILABLE;
            return;
        }

        $gremlinJar = glob("{$this->config->gsneo4jv3_folder}/lib/gremlin-core-*.jar");
        $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
        // 3.4/3.5
        preg_match('/gremlin-core-(\d+\.\d+)\.\d+.jar/', $gremlinVersion, $r);
        $this->gremlinVersion = $r[1] ?? 'gremlin-core not found';
        if(!in_array($this->gremlinVersion, self::GREMLIN_VERSIONS, STRICT_COMPARISON)) {
            throw new UnknownGremlinVersion($this->gremlinVersion);
        }

        $this->db = new Connection(array( 'host'     => $this->config->gsneo4jv3_host,
                                          'port'     => $this->config->gsneo4jv3_port,
                                          'graph'    => 'graph',
                                          'emptySet' => true,
                                   ) );

        $this->db->message->registerSerializer('\Exakat\Graph\Helpers\GraphsonV3', true);
        $this->status = self::UNCHECKED;
    }

    private function checkConfiguration(): void {
        ini_set('default_socket_timeout', '1600');
        $this->db->open();
    }

    public function query(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNAVAILABLE) {
            return new GraphResults();
        }

        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $params['#jsr223.groovy.engine.keep.globals'] = 'phantom';
        foreach ($params as $name => $value) {
            $this->db->message->bindValue($name, $value);
        }

        $result = $this->db->send($query);


        return new GraphResults($result);
    }

    public function queryOne(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        return $this->query($query, $params, $load);
    }

    public function checkConnection(): bool {
        $res = @stream_socket_client("tcp://{$this->config->gsneo4jv3_host}:{$this->config->gsneo4jv3_port}",
                                     $errno,
                                     $errorMessage,
                                     1,
                                     STREAM_CLIENT_CONNECT
                                     );

        return is_resource($res);
    }

    public function serverInfo(): array {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $res = $this->query('Gremlin.version();');

        return $res->toArray();
    }

    public function clean(): void {
        $this->stop();
        $this->start();
    }

    public function start(): void {
        if (!file_exists("{$this->config->gsneo4jv3_folder}/conf")) {
            throw new GremlinException('No graphdb found.');
        }

        if (!file_exists("{$this->config->gsneo4jv3_folder}/conf/gsneo4jv3.{$this->gremlinVersion}.yaml")) {
            assert(file_exists("{$this->config->dir_root}/server/gsneo4jv3/gsneo4jv3.{$this->gremlinVersion}.yaml"),
                   "Missing gsneo4jv3.{$this->gremlinVersion}.yaml in server/gsneo4jv3");
            copy( "{$this->config->dir_root}/server/gsneo4jv3/gsneo4jv3.{$this->gremlinVersion}.yaml",
                  "{$this->config->gsneo4jv3_folder}/conf/gsneo4jv3.{$this->gremlinVersion}.yaml");
            copy( "{$this->config->dir_root}/server/gsneo4jv3/exakat.properties",
                  "{$this->config->gsneo4jv3_folder}/conf/exakat.properties");
        }

        if (in_array($this->gremlinVersion, self::GREMLIN_VERSIONS)) {
            display("start gremlin server {$this->gremlinVersion}.x");
            putenv("GREMLIN_YAML=conf/gsneo4jv3.{$this->gremlinVersion}.yaml");
            putenv('PID_DIR=db');
            exec("GREMLIN_YAML=conf/gsneo4jv3.{$this->gremlinVersion}.yaml; PID_DIR=db; cd {$this->config->gsneo4jv3_folder}; rm -rf db/neo4j; /bin/bash ./bin/gremlin-server.sh start > gremlin.log 2>&1 &");
        }
        display('started gremlin server');
        $this->init();
        sleep(2);

        $timer = new Timer();
        $round = -1;
        $pid = false;
        do {
            $connexion = $this->checkConnection();
            if (!$connexion) {
                ++$round;
                usleep(100000 * $round);
            }
        } while ( !$connexion && $round < 20);
        $timer->end();

        display("Restarted in $round rounds\n");

        if (file_exists("{$this->config->gsneo4jv3_folder}/db/gremlin.pid")) {
            $pid = trim(file_get_contents("{$this->config->gsneo4jv3_folder}/db/gremlin.pid"));
        } elseif ( file_exists("{$this->config->gsneo4jv3_folder}/db/gsneo4jv3.pid")) {
            $pid = trim(file_get_contents("{$this->config->gsneo4jv3_folder}/db/gsneo4jv3.pid"));
        } else {
            $pid = false;
        }

        $ms = number_format($timer->duration(Timer::MS), 2);
        $pid = $pid === false ? 'Not yet' : $pid;
        display("started [$pid] in $ms ms");
    }

    public function stop(): void {
        if (file_exists("{$this->config->gsneo4jv3_folder}/db/gremlin.pid")) {
            display('stop gremlin server ' . $this->gremlinVersion . '.x');
            putenv('GREMLIN_YAML=conf/gsneo4jv3.' . $this->gremlinVersion . '.yaml');
            putenv('PID_DIR=db');
            shell_exec("GREMLIN_YAML=conf/gsneo4jv3.{$this->gremlinVersion}.yaml; PID_DIR=db; cd {$this->config->gsneo4jv3_folder}; /bin/bash ./bin/gremlin-server.sh stop; rm -rf db/gremlin.pid");
        }
    }

    public function fixId($id) {
        if ($this->initialId === null) {
            $id = $this->query('g.addV("X").id()')->toInt();
            $this->query('g.V(' . $id . ').drop()');

            $this->initialId = $id + 1;
        }
        return $id - 1 + $this->initialId;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph;

use Exakat\Graph\Helpers\GraphResults;

abstract class Graph {
    protected $config = null;

    protected $initialId = null;

    public const GRAPHDB = array('nogremlin',
                                 'gsneo4j',
                                 'gsneo4jV3',
                                 'tinkergraph',
                                 'tinkergraphv3',
                                 );

    public function __construct() {
        $this->config = exakat('config');
    }

    abstract public function query(string $query, array $params = array(),array $load = array()): GraphResults;

    abstract public function queryOne(string $query, array $params = array(),array $load = array()): GraphResults;

    abstract public function init(): void;

    abstract public function getInfo(): array;

    abstract public function start(): void;

    abstract public function stop(): void;

    public function restart(): void {
        $this->stop();
        $this->start();
    }

    abstract public function serverInfo(): array;

    abstract public function checkConnection(): bool;

    abstract public function clean(): void;

    public function empty(): void {
        // don't catch max(id) here
        $this->query('g.V().drop();');
        $this->query('g.E().drop();');
    }

    // Produces an id for storing a new value.
    // null means that the graph will handle it.
    // This is not the case of all graph : tinkergraph doesn't.
    public function getId() {
        return 'null';
    }

    public function fixId($id) {
        return $id + $this->initialId;
    }

    public static function getConnexion(string $gremlin = null): self {
        if ($gremlin === null) {
            $config = exakat('config');
            $gremlin = $config->gremlin;
        }

        $graphDBClass = "\\Exakat\\Graph\\$gremlin";

        return new $graphDBClass();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph;

use Exakat\Graph\Helpers\GraphResults;
use Exakat\Exceptions\GremlinException;
use Exakat\Exceptions\UnknownGremlinVersion;
use Brightzone\GremlinDriver\Connection;
use Exakat\Helpers\Timer;
use stdClass;

class GSNeo4j extends Graph {
    public const CHECKED     = true;
    public const UNCHECKED   = false;
    public const UNAVAILABLE = 2;

    private $status     = self::UNCHECKED;

    private $db         = null;

    private $gremlinVersion = '3.4';

    public function getInfo(): array {
        $stats = array();

        if (empty($this->config->gsneo4j_folder)) {
            $stats['configured'] = 'No gsneo4j_folder configured in config/exakat.ini.';
        } elseif (!file_exists($this->config->gsneo4j_folder)) {
            $stats['installed'] = 'No (folder : ' . $this->config->gsneo4j_folder . ')';
        } elseif (!file_exists($this->config->gsneo4j_folder . '/ext/neo4j-gremlin/')) {
            $stats['installed'] = 'Partially (missing neo4j folder : ' . $this->config->gsneo4j_folder . ')';
        } else {
            $stats['installed'] = "Yes (folder : {$this->config->gsneo4j_folder})";
            $stats['host'] = $this->config->gsneo4j_host;
            $stats['port'] = $this->config->gsneo4j_port;

            $plugins = glob("{$this->config->gsneo4j_folder}/ext/neo4j-gremlin/plugin/*.jar");
            if (count($plugins) !== 72) {
                $stats['grapes failed'] = 'Partially installed neo4j plugin. Please, check installation docs, and "grab" again : some of the files are missing for neo4j.';
            }

            $gremlinJar = glob("{$this->config->gsneo4j_folder}/lib/gremlin-core-*.jar");
            $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
            //gremlin-core-3.2.5.jar
            $gremlinVersion = substr($gremlinVersion, 13, -4);
            $stats['gremlin version'] = $gremlinVersion;

            $neo4jJar = glob("{$this->config->gsneo4j_folder}/ext/neo4j-gremlin/lib/neo4j-*.jar");
            $neo4jJar = array_filter($neo4jJar, function (string $x): bool { return (bool) preg_match('#/neo4j-\d\.\d\.\d\.jar#', $x); });
            $neo4jVersion = basename(array_pop($neo4jJar) ?? '');

            //neo4j-2.3.3.jar
            $neo4jVersion = substr($neo4jVersion, 6, -4);
            $stats['neo4j version'] = $neo4jVersion;

            if (file_exists("{$this->config->gsneo4j_folder}/db/gsneo4j.pid")) {
                $stats['running'] = 'Yes (PID : ' . trim(file_get_contents("{$this->config->gsneo4j_folder}/db/gsneo4j.pid")) . ')';
            }
        }

        return $stats;
    }

    public function init(): void {
        if (!file_exists("{$this->config->gsneo4j_folder}/lib/")) {
            // No local production, just skip init.
            $this->status = self::UNAVAILABLE;
            return;
        }

        $gremlinJar = glob("{$this->config->gsneo4j_folder}/lib/gremlin-core-*.jar");
        $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
        // 3.4 or 3.3 or 3.2
        $this->gremlinVersion = substr($gremlinVersion, 13, -6);
        if(!in_array($this->gremlinVersion, array('3.2', '3.3', '3.4'), STRICT_COMPARISON)) {
            throw new UnknownGremlinVersion($this->gremlinVersion);
        }

        $this->db = new Connection(array( 'host'     => $this->config->gsneo4j_host,
                                          'port'     => $this->config->gsneo4j_port,
                                          'graph'    => 'graph',
                                          'emptySet' => true,
                                   ) );
        $this->status = self::UNCHECKED;
    }

    private function checkConfiguration(): void {
        ini_set('default_socket_timeout', '1600');
        $this->db->open();
    }

    public function query(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNAVAILABLE) {
            return new GraphResults();
        }

        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $params['#jsr223.groovy.engine.keep.globals'] = 'phantom';
        foreach ($params as $name => $value) {
            $this->db->message->bindValue($name, $value);
        }

        $result = $this->db->send($query);

        if (empty($result)) {
            return new GraphResults();
        } elseif ($result[0] === null) {
            return new GraphResults();
        } elseif (is_array($result[0])) {
            if (isset($result[0]['type'])) {
                $result = $this->simplifyArray($result);
            }

            return new GraphResults($result);
        } elseif (is_array($result)) {
            return new GraphResults($result);
        } elseif ($result instanceof stdClass) {
            return new GraphResults($result);
        } else {
            print 'Processing unknown type ' . gettype($result) . PHP_EOL;
            die(__METHOD__);
        }
    }

    public function queryOne(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        return $this->query($query, $params, $load);
    }

    public function checkConnection(): bool {
        $res = @stream_socket_client("tcp://{$this->config->gsneo4j_host}:{$this->config->gsneo4j_port}",
                                     $errno,
                                     $errorMessage,
                                     1,
                                     STREAM_CLIENT_CONNECT
                                     );

        return is_resource($res);
    }

    public function serverInfo(): array {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $res = $this->query('Gremlin.version();');

        return $res->toArray();
    }

    public function clean(): void {
        $this->stop();
        $this->start();
    }

    public function start(): void {
        if (!file_exists("{$this->config->gsneo4j_folder}/conf")) {
            throw new GremlinException('No graphdb found.');
        }

        if (!file_exists("{$this->config->gsneo4j_folder}/conf/gsneo4j.{$this->gremlinVersion}.yaml")) {
            copy( "{$this->config->dir_root}/server/gsneo4j/gsneo4j.{$this->gremlinVersion}.yaml",
                  "{$this->config->gsneo4j_folder}/conf/gsneo4j.{$this->gremlinVersion}.yaml");
            copy( "{$this->config->dir_root}/server/gsneo4j/exakat.properties",
                  "{$this->config->gsneo4j_folder}/conf/exakat.properties");
        }

        if (in_array($this->gremlinVersion, array('3.3', '3.4'))) {
            display("start gremlin server {$this->gremlinVersion}.x");
            putenv("GREMLIN_YAML=conf/gsneo4j.{$this->gremlinVersion}.yaml");
            putenv('PID_DIR=db');
            exec("GREMLIN_YAML=conf/gsneo4j.{$this->gremlinVersion}.yaml; PID_DIR=db; cd {$this->config->gsneo4j_folder}; rm -rf db/neo4j;/bin/bash ./bin/gremlin-server.sh start > gremlin.log 2>&1 &");
        } elseif ($this->gremlinVersion === '3.2') {
            display('start gremlin server 3.2.x');
            exec("cd {$this->config->gsneo4j_folder}; rm -rf db/neo4j;/bin/bash ./bin/gremlin-server.sh conf/gsneo4j.3.2.yaml  > gremlin.log 2>&1 & echo $! > db/gsneo4j.pid ");
        }
        display('started gremlin server');
        $this->init();
        sleep(2);

        $timer = new Timer();
        $round = -1;
        $pid = false;
        do {
            $connexion = $this->checkConnection();
            if (empty($connexion)) {
                ++$round;
                usleep(100000 * $round);
            }
        } while ( empty($connexion) && $round < 20);
        $timer->end();

        display("Restarted in $round rounds\n");

        if (file_exists("{$this->config->gsneo4j_folder}/db/gremlin.pid")) {
            $pid = trim(file_get_contents("{$this->config->gsneo4j_folder}/db/gremlin.pid"));
        } elseif ( file_exists("{$this->config->gsneo4j_folder}/db/gsneo4j.pid")) {
            $pid = trim(file_get_contents("{$this->config->gsneo4j_folder}/db/gsneo4j.pid"));
        } else {
            $pid = false;
        }

        $ms = number_format($timer->duration(Timer::MS), 2);
        $pid = $pid === false ? 'Not yet' : $pid;
        display("started [$pid] in $ms ms");
    }

    public function stop(): void {
        if (file_exists("{$this->config->gsneo4j_folder}/db/gremlin.pid")) {
            display('stop gremlin server 3.3.x');
            putenv('GREMLIN_YAML=conf/gsneo4j.3.3.yaml');
            putenv('PID_DIR=db');
            shell_exec("GREMLIN_YAML=conf/gsneo4j.3.3.yaml; PID_DIR=db; cd {$this->config->gsneo4j_folder}; ./bin/gremlin-server.sh stop; rm -rf db/gremlin.pid");
        }

        if (file_exists("{$this->config->gsneo4j_folder}/db/gsneo4j.pid")) {
            display('stop gremlin server 3.2.x');
            shell_exec("kill -9 \$(cat {$this->config->gsneo4j_folder}/db/gsneo4j.pid) 2>> gremlin.log; rm -f {$this->config->gsneo4j_folder}/db/gsneo4j.pid");
        }
    }

    private function simplifyArray($result) {
        $return = array();

        if (!isset($result[0]['properties'])) {
            return $result;
        }

        foreach ($result as $r) {
            $row = array('id'    => $r['id'],
                         'label' => $r['label']);
            foreach ($r['properties'] as $property => $value) {
                $row[$property] = $value[0]['value'];
            }

            $return[] = $row;
        }

        return $return;
    }

    public function fixId($id) {
        if ($this->initialId === null) {
            $id = $this->query('g.addV("X").id()')->toInt();
            $this->query('g.V(' . $id . ').drop()');

            $this->initialId = $id + 1;
        }
        return $id - 1 + $this->initialId;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph;

use Exakat\Graph\Helpers\GraphResults;
use Exakat\Exceptions\GremlinException;
use Exakat\Exceptions\UnknownGremlinVersion;
use Brightzone\GremlinDriver\Connection;
use Exakat\Helpers\Timer;

class TinkergraphV3 extends Graph {
    public const CHECKED     = true;
    public const UNCHECKED   = false;
    public const UNAVAILABLE = 1;

    private $status   = self::UNCHECKED;
    private $db       = null;

    private $gremlinVersion = '3.4';

    public function init(): void {
        if (!file_exists("{$this->config->tinkergraphv3_folder}/lib/")) {
            // No local production, just skip init.
            $this->status = self::UNAVAILABLE;
            return;
        }

        $gremlinJar = glob("{$this->config->tinkergraphv3_folder}/lib/gremlin-core-*.jar");
        $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
        //gremlin-core-3.4.10.jar
        $gremlinVersion = substr($gremlinVersion, 13, -4);
        $stats['gremlin version'] = $gremlinVersion;

        if(!in_array($this->gremlinVersion, array('3.4'), STRICT_COMPARISON)) {
            throw new UnknownGremlinVersion($this->gremlinVersion);
        }

        $this->db = new Connection(array( 'host'  => $this->config->tinkergraphv3_host,
                                          'port'  => $this->config->tinkergraphv3_port,
                                          'graph' => 'graph',
                                          'emptySet' => true,
                                   ) );
        $this->db->message->registerSerializer('\Exakat\Graph\Helpers\GraphsonV3', true);
        $this->status = self::UNCHECKED;
    }

    public function getInfo(): array {
        $stats = array();

        if (empty($this->config->tinkergraphv3_folder)) {
            $stats['configured'] = 'No tinkergraph configured in config/exakat.ini.';
        } elseif (!file_exists($this->config->tinkergraphv3_folder)) {
            $stats['installed'] = 'No (folder : ' . $this->config->tinkergraphv3_folder . ')';
        } else {
            $stats['installed'] = 'Yes (folder : ' . $this->config->tinkergraphv3_folder . ')';
            $stats['host'] = $this->config->tinkergraph_host;
            $stats['port'] = $this->config->tinkergraph_port;

            $gremlinJar = glob("{$this->config->tinkergraphv3_folder}/lib/gremlin-core-*.jar");
            $gremlinVersion = basename(array_pop($gremlinJar) ?? '');
            //example : gremlin-core-3.2.5.jar
            $gremlinVersion = substr($gremlinVersion, 13, -4);

            $stats['gremlin version'] = $gremlinVersion;

            if (file_exists("{$this->config->tinkergraph_port}/db/tinkergraph.pid")) {
                $stats['running'] = 'Yes (PID : ' . trim(file_get_contents("{$this->config->tinkergraph_port}/db/tinkergraph.pid")) . ')';
            }
        }

        return $stats;
    }

    private function checkConfiguration(): void {
        ini_set('default_socket_timeout', '1600');
        $this->db->open();
    }

    public function query(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNAVAILABLE) {
            return new GraphResults();
        } elseif ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $params['#jsr223.groovy.engine.keep.globals'] = 'phantom';
        foreach ($params as $name => $value) {
            $this->db->message->bindValue($name, $value);
        }

        $result = $this->db->send($query);

        return new GraphResults($result);
    }

    public function queryOne(string $query, array $params = array(),array $load = array()): GraphResults {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        return $this->query($query, $params, $load);
    }

    public function checkConnection(): bool {
        $res = @stream_socket_client('tcp://' . $this->config->tinkergraph_host . ':' . $this->config->tinkergraph_port,
                                     $errno,
                                     $errorMessage,
                                     1,
                                     STREAM_CLIENT_CONNECT
                                     );

        return is_resource($res);
    }

    public function serverInfo(): array {
        if ($this->status === self::UNCHECKED) {
            $this->checkConfiguration();
        }

        $res = $this->query('Gremlin.version();');

        return $res->toArray();
    }

    public function clean(): void {
        // This is memory only Database
        $this->stop();
        $this->start();
    }

    public function start(): void {
        if (!file_exists("{$this->config->tinkergraphv3_folder}/conf")) {
            throw new GremlinException('No tinkgergraph configuration folder found.');
        }

        if (!file_exists("{$this->config->tinkergraphv3_folder}/conf/tinkergraphv3.{$this->gremlinVersion}.yaml")) {
            copy("{$this->config->dir_root}/server/tinkergraphv3/tinkergraphv3.{$this->gremlinVersion}.yaml",
                 "{$this->config->tinkergraphv3_folder}/conf/tinkergraphv3.{$this->gremlinVersion}.yaml");
        }

        if (in_array($this->gremlinVersion, array('3.4'), STRICT_COMPARISON)) {
            putenv("GREMLIN_YAML=conf/tinkergraphv3.{$this->gremlinVersion}.yaml");
            putenv('PID_DIR=db');
            exec("GREMLIN_YAML=conf/tinkergraphv3.{$this->gremlinVersion}.yaml; PID_DIR=db; cd {$this->config->tinkergraphv3_folder}; rm -rf db/neo4j; /bin/bash  ./bin/gremlin-server.sh start > gremlin.log 2>&1 &");
        } else {
            throw new GremlinException("Wrong version for tinkergraph : $this->gremlinVersion");
        }
        $this->init();
        sleep(1);

        $timer = new Timer();
        $round = -1;
        do {
            $res = $this->checkConnection();
            ++$round;
            usleep(100000 * $round);
        } while (!$res && $round < 20);
        $timer->end();

        display("Restarted in $round rounds\n");

        if (file_exists("{$this->config->tinkergraphv3_folder}/run/gremlin.pid")) {
            $pid = trim(file_get_contents("{$this->config->tinkergraphv3_folder}/run/gremlin.pid"));
        } elseif (file_exists("{$this->config->tinkergraphv3_folder}/db/tinkergraph.pid")) {
            $pid = trim(file_get_contents("{$this->config->tinkergraphv3_folder}/db/tinkergraph.pid"));
        } else {
            $pid = 'Not yet';
        }

        display('started [' . $pid . '] in ' . number_format($timer->duration(Timer::MS), 2) . ' ms' );
    }

    public function stop(): void {
        if (file_exists("{$this->config->tinkergraphv3_folder}/db/gremlin.pid")) {
            display("stop gremlin server {$this->gremlinVersion}");
            putenv("GREMLIN_YAML=conf/tinkergraphv3.{$this->gremlinVersion}.yaml");
            putenv('PID_DIR=db');
            shell_exec("cd {$this->config->tinkergraphv3_folder}; ./bin/gremlin-server.sh stop");
            if (file_exists("{$this->config->tinkergraphv3_folder}/db/gremlin.pid")) {
                unlink("{$this->config->tinkergraphv3_folder}/db/gremlin.pid");
            }
        }

        if (file_exists("{$this->config->tinkergraphv3_folder}/db/tinkergraph.pid")) {
            display('stop gremlin server 3.2');
            shell_exec("kill -9 $(cat {$this->config->tinkergraphv3_folder}/db/tinkergraph.pid) 2>> gremlin.log; rm -f {$this->config->tinkergraphv3_folder}/db/tinkergraph.pid");
        }
    }

    public function getDefinitionSQL(): string {
        // Optimize loading by sorting the results
        return <<<'SQL'
SELECT DISTINCT CASE WHEN definitions.id IS NULL THEN definitions2.id ELSE definitions.id END AS definition, GROUP_CONCAT(DISTINCT calls.id) AS call, count(calls.id) AS id
FROM calls
LEFT JOIN definitions 
    ON definitions.type       = calls.type       AND
       definitions.fullnspath = calls.fullnspath
LEFT JOIN definitions definitions2
    ON definitions2.type       = calls.type       AND
       definitions2.fullnspath = calls.globalpath 
WHERE (definitions.id IS NOT NULL OR definitions2.id IS NOT NULL)
GROUP BY definition
SQL;
    }

    public function getGlobalsSql(): string {
        return 'SELECT origin, destination FROM globals';
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*//**
 * Original work from Ignas Bernotas
 * Modification and reduction by Damien Seguy
 * Copyright (C) 2014, 2015 Textalk
 * Copyright (C) 2015 Ignas Bernotas - added context options and handling
 *
 * This file is part of Websocket PHP and is free software under the ISC License.
 * License text: https://raw.githubusercontent.com/Textalk/websocket-php/master/COPYING
 */

namespace Exakat\Graph\Helpers;

class Websocket {
    protected $socket, $is_connected = false, $is_closing = false, $last_opcode = null,
    $close_status = null, $huge_payload = null;

    protected static $opcodes = array(
    'continuation' => 0,
    'text'         => 1,
    'binary'       => 2,
    'close'        => 8,
    'ping'         => 9,
    'pong'         => 10,
  );

    public function getLastOpcode() {
        return $this->last_opcode;
    }
    public function getCloseStatus() {
        return $this->close_status;
    }
    public function isConnected() {
        return $this->is_connected;
    }

    public function setTimeout($timeout) {
        $this->options['timeout'] = $timeout;

        if ($this->socket && get_resource_type($this->socket) === 'stream') {
            stream_set_timeout($this->socket, $timeout);
        }
    }

    public function setFragmentSize($fragment_size) {
        $this->options['fragment_size'] = $fragment_size;
        return $this;
    }

    public function getFragmentSize() {
        return $this->options['fragment_size'];
    }

    public function send($payload, $opcode = 'text', $masked = true) {
        if (!$this->is_connected) {
            $this->connect();
        } /// @todo This is a client function, fixme!

        if (!in_array($opcode, array_keys(self::$opcodes))) {
            throw new \Exception("Bad opcode '$opcode'.  Try 'text' or 'binary'.");
        }

        // record the length of the payload
        $payload_length = strlen($payload);

        $fragment_cursor = 0;
        // while we have data to send
        while ($payload_length > $fragment_cursor) {
            // get a fragment of the payload
            $sub_payload = substr($payload, $fragment_cursor, $this->options['fragment_size']);

            // advance the cursor
            $fragment_cursor += $this->options['fragment_size'];

            // is this the final fragment to send?
            $final = $payload_length <= $fragment_cursor;

            // send the fragment
            $this->send_fragment($final, $sub_payload, $opcode, $masked);

            // all fragments after the first will be marked a continuation
            $opcode = 'continuation';
        }
    }

    protected function send_fragment($final, $payload, $opcode, $masked) {
        // Binary string for header.
        $frame_head_binstr = '';

        // Write FIN, final fragment bit.
        $frame_head_binstr .= (bool) $final ? '1' : '0';

        // RSV 1, 2, & 3 false and unused.
        $frame_head_binstr .= '000';

        // Opcode rest of the byte.
        $frame_head_binstr .= sprintf('%04b', self::$opcodes[$opcode]);

        // Use masking?
        $frame_head_binstr .= $masked ? '1' : '0';

        // 7 bits of payload length...
        $payload_length = strlen($payload);
        if ($payload_length > 65535) {
            $frame_head_binstr .= decbin(127);
            $frame_head_binstr .= sprintf('%064b', $payload_length);
        } elseif ($payload_length > 125) {
            $frame_head_binstr .= decbin(126);
            $frame_head_binstr .= sprintf('%016b', $payload_length);
        } else {
            $frame_head_binstr .= sprintf('%07b', $payload_length);
        }

        $frame = '';

        // Write frame head to frame.
        foreach (str_split($frame_head_binstr, 8) as $binstr) {
            $frame .= chr(bindec($binstr));
        }

        // Handle masking
        if ($masked) {
            // generate a random mask:
            $mask = '';
            for ($i = 0; $i < 4; ++$i) {
                $mask .= chr(rand(0, 255));
            }
            $frame .= $mask;
        }

        // Append payload to frame:
        for ($i = 0; $i < $payload_length; ++$i) {
            $frame .= ($masked === true) ? $payload[$i] ^ $mask[$i % 4] : $payload[$i];
        }

        $this->write($frame);
    }

    public function receive() {
        if (!$this->is_connected) {
            $this->connect();
        } /// @todo This is a client function, fixme!

        $this->huge_payload = '';

        $response = null;
        while (is_null($response)) {
            $response = $this->receive_fragment();
        }

        return $response;
    }

    protected function receive_fragment() {

    // Just read the main fragment information first.
        $data = $this->read(2);

        // Is this the final fragment?  // Bit 0 in byte 0
        /// @todo Handle huge payloads with multiple fragments.
        $final = (boolean) (ord($data[0]) & 1 << 7);

        // Should be unused, and must be false…  // Bits 1, 2, & 3
        $rsv1  = (boolean) (ord($data[0]) & 1 << 6);
        $rsv2  = (boolean) (ord($data[0]) & 1 << 5);
        $rsv3  = (boolean) (ord($data[0]) & 1 << 4);

        // Parse opcode
    $opcode_int = ord($data[0]) & 31; // Bits 4-7
    $opcode_ints = array_flip(self::$opcodes);
        if (!array_key_exists($opcode_int, $opcode_ints)) {
            throw new \Exception("Bad opcode in websocket frame: $opcode_int");
        }
        $opcode = $opcode_ints[$opcode_int];

        // record the opcode if we are not receiving a continutation fragment
        if ($opcode !== 'continuation') {
            $this->last_opcode = $opcode;
        }

        // Masking?
    $mask = (boolean) (ord($data[1]) >> 7);  // Bit 0 in byte 1

    $payload = '';

        // Payload length
    $payload_length = (integer) ord($data[1]) & 127; // Bits 1-7 in byte 1
    if ($payload_length > 125) {
        if ($payload_length === 126) {
            $data = $this->read(2);
        } // 126: Payload is a 16-bit unsigned int
      else {
          $data = $this->read(8);
      } // 127: Payload is a 64-bit unsigned int
      $payload_length = bindec(self::sprintB($data));
    }

        // Get masking key.
        if ($mask) {
            $masking_key = $this->read(4);
        }

        // Get the actual payload, if any (might not be for e.g. close frames.
        if ($payload_length > 0) {
            $data = $this->read($payload_length);

            if ($mask) {
                // Unmask payload.
                for ($i = 0; $i < $payload_length; ++$i) {
                    $payload .= ($data[$i] ^ $masking_key[$i % 4]);
                }
            } else {
                $payload = $data;
            }
        }

        if ($opcode === 'close') {
            // Get the close status.
            if ($payload_length >= 2) {
                $status_bin = $payload[0] . $payload[1];
                $status = bindec(sprintf('%08b%08b', ord($payload[0]), ord($payload[1])));
                $this->close_status = $status;
                $payload = substr($payload, 2);

                if (!$this->is_closing) {
                    $this->send($status_bin . 'Close acknowledged: ' . $status, 'close', true);
                } // Respond.
            }

            if ($this->is_closing) {
                $this->is_closing = false;
            } // A close response, all done.

            // And close the socket.
            fclose($this->socket);
            $this->is_connected = false;
        }

        // if this is not the last fragment, then we need to save the payload
        if (!$final) {
            $this->huge_payload .= $payload;
            return null;
        }
        // this is the last fragment, and we are processing a huge_payload
        elseif ($this->huge_payload) {
            // sp we need to retreive the whole payload
            $payload = $this->huge_payload .= $payload;
            $this->huge_payload = null;
        }

        return $payload;
    }

    /**
     * Tell the socket to close.
     *
     * @param integer $status  http://tools.ietf.org/html/rfc6455#section-7.4
     * @param string  $message A closing message, max 125 bytes.
     */
    public function close($status = 1000, $message = 'ttfn') {
        $status_binstr = sprintf('%016b', $status);
        $status_str = '';
        foreach (str_split($status_binstr, 8) as $binstr) {
            $status_str .= chr(bindec($binstr));
        }
        $this->send($status_str . $message, 'close', true);

        $this->is_closing = true;
        $response = $this->receive(); // Receiving a close frame will close the socket now.

        return $response;
    }

    protected function write($data) {
        $written = fwrite($this->socket, $data);

        if ($written < strlen($data)) {
            throw new \Exception(
        "Could only write $written out of " . strlen($data) . ' bytes.'
      );
        }
    }

    protected function read($length) {
        $data = '';
        while (strlen($data) < $length) {
            $buffer = fread($this->socket, $length - strlen($data));
            if ($buffer === false) {
                $metadata = stream_get_meta_data($this->socket);
                throw new \Exception(
          'Broken frame, read ' . strlen($data) . ' of stated '
          . $length . ' bytes.  Stream state: '
          . json_encode($metadata)
        );
            }
            if ($buffer === '') {
                $metadata = stream_get_meta_data($this->socket);
                throw new \Exception(
          'Empty read; connection dead?  Stream state: ' . json_encode($metadata)
        );
            }
            $data .= $buffer;
        }

        return $data;
    }


    /**
     * Helper to convert a binary to a string of '0' and '1'.
     */
    protected static function sprintB($string) {
        $return = '';
        for ($i = 0; $i < strlen($string); ++$i) {
            $return .= sprintf('%08b', ord($string[$i]));
        }
        return $return;
    }

    protected $socket_uri;

    /**
     * @param string  $uri      A ws/wss-URI
     * @param array   $options
     *   Associative array containing:
     *   - context:      Set the stream context. Default: empty context
     *   - timeout:      Set the socket timeout in seconds.  Default: 5
     *   - headers:      Associative array of headers to set/override.
     */
    public function __construct($uri, $options = array()) {
        $this->options = $options;

        if (!array_key_exists('timeout', $this->options)) {
            $this->options['timeout'] = 5;
        }

        // the fragment size
        if (!array_key_exists('fragment_size', $this->options)) {
            $this->options['fragment_size'] = 4096;
        }

        $this->socket_uri = $uri;
    }

    public function __destruct() {
        if ($this->socket) {
            if (get_resource_type($this->socket) === 'stream') {
                fclose($this->socket);
            }
            $this->socket = null;
        }
    }

    /**
     * Perform WebSocket handshake
     */
    protected function connect() {
        $url_parts = parse_url($this->socket_uri);
        $scheme    = $url_parts['scheme'];
        $host      = $url_parts['host'];
        $user      = isset($url_parts['user']) ? $url_parts['user'] : '';
        $pass      = isset($url_parts['pass']) ? $url_parts['pass'] : '';
        $port      = isset($url_parts['port']) ? $url_parts['port'] : ($scheme === 'wss' ? 443 : 80);
        $path      = isset($url_parts['path']) ? $url_parts['path'] : '/';
        $query     = isset($url_parts['query']) ? $url_parts['query'] : '';
        $fragment  = isset($url_parts['fragment']) ? $url_parts['fragment'] : '';

        $path_with_query = $path;
        if (!empty($query)) {
            $path_with_query .= '?' . $query;
        }
        if (!empty($fragment)) {
            $path_with_query .= '#' . $fragment;
        }

        if (!in_array($scheme, array('ws', 'wss'))) {
            throw new \Exception(
        "Url should have scheme ws or wss, not '$scheme' from URI '$this->socket_uri' ."
      );
        }

        $host_uri = ($scheme === 'wss' ? 'ssl' : 'tcp') . '://' . $host;

        // Set the stream context options if they're already set in the config
        if (isset($this->options['context'])) {
            // Suppress the error since we'll catch it below
            if (@get_resource_type($this->options['context']) === 'stream-context') {
                $context = $this->options['context'];
            } else {
                throw new \InvalidArgumentException(
          "Stream context in \$options['context'] isn't a valid context"
        );
            }
        } else {
            $context = stream_context_create();
        }

        // Open the socket.  @ is there to supress warning that we will catch in check below instead.
        $this->socket = @stream_socket_client(
      $host_uri . ':' . $port,
      $errno,
      $errstr,
      $this->options['timeout'],
      STREAM_CLIENT_CONNECT,
      $context
    );

        if ($this->socket === false) {
            throw new \Exception(
        "Could not open socket to \"$host:$port\": $errstr ($errno)."
      );
        }

        // Set timeout on the stream as well.
        stream_set_timeout($this->socket, $this->options['timeout']);

        // Generate the WebSocket key.
        $key = self::generateKey();

        // Default headers (using lowercase for simpler array_merge below).
        $headers = array(
      'host'                  => $host . ':' . $port,
      'user-agent'            => 'websocket-client-php',
      'connection'            => 'Upgrade',
      'upgrade'               => 'websocket',
      'sec-websocket-key'     => $key,
      'sec-websocket-version' => '13',
    );

        // Handle basic authentication.
        if ($user || $pass) {
            $headers['authorization'] = 'Basic ' . base64_encode($user . ':' . $pass) . "\r\n";
        }

        // Deprecated way of adding origin (use headers instead).
        if (isset($this->options['origin'])) {
            $headers['origin'] = $this->options['origin'];
        }

        // Add and override with headers from options.
        if (isset($this->options['headers'])) {
            $headers = array_merge($headers, array_change_key_case($this->options['headers']));
        }

        $header =
      'GET ' . $path_with_query . " HTTP/1.1\r\n"
      . implode(
        "\r\n", array_map(
          function ($key, $value) {
              return "$key: $value";
          }, array_keys($headers), $headers
        )
      )
      . "\r\n\r\n";

        // Send headers.
        $this->write($header);

        // Get server response header (terminated with double CR+LF).
        $response = stream_get_line($this->socket, 1024, "\r\n\r\n");

        /// @todo Handle version switching

        // Validate response.
        if (!preg_match('#Sec-WebSocket-Accept:\s(.*)$#mUi', $response, $matches)) {
            $address = $scheme . '://' . $host . $path_with_query;
            throw new \Exception(
        "Connection to '{$address}' failed: Server sent invalid upgrade response:\n"
        . $response
      );
        }

        $keyAccept = trim($matches[1]);
        $expectedResonse
      = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));

        if ($keyAccept !== $expectedResonse) {
            throw new \Exception('Server sent bad upgrade response.');
        }

        $this->is_connected = true;
    }

    /**
     * Generate a random string for WebSocket key.
     * @return string Random string
     */
    protected static function generateKey() {
        $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"$&/()=[]{}0123456789';
        $key = '';
        $chars_length = strlen($chars);
        for ($i = 0; $i < 16; ++$i) {
            $key .= $chars[mt_rand(0, $chars_length-1)];
        }
        return base64_encode($key);
    }
}
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Graph\Helpers;

use stdClass;

class GraphResults implements \ArrayAccess, \Iterator, \Countable {
    public const EMPTY   = 0;
    public const SCALAR  = 1;
    public const ARRAY   = 2;

    private $type = self::EMPTY;
    private $data  = null;

    public function __construct($data = null) {
        // Case of empty result set.

//        print "\nExtracted from JSON\n";
//        var_dump($data);
//        print "\nExtracted from JSON------\n";

// A garder. Aucun résultat.
        if ($data === null) {
            $this->type = self::EMPTY;
            $this->data = $data;

            return;
        }

// A garder. liste de résultats
        if (is_array($data)) {
            if (!isset($data[0]) || ($data[0] === null)) {
                $this->type = self::EMPTY;
                $this->data = null;

            } else {
                $this->type = self::ARRAY;
                $this->data = $data;
                $this->checkArray();
            }

            return;
        }

        assert(false, 'Could not understand GraphResults incoming data');
    }

    private function checkArray(): void {
        if (empty($this->data)) {
            return;
        }
        $data = array_values($this->data);
        if (!($data[0] instanceof stdClass)) {
            return;
        }

        foreach ($this->data as &$data) {
            $data = (array) $data;
        }
        unset($data);
    }

    public function deHash(array $extra = null) {
        if (empty($this->data)) {
            return;
        }

        $result = array();
        foreach($this->data as $value) {
            foreach($value as $k => $v) {
                $result[] = array('', $k, $v);
            }
        }
        if ($extra !== null) {
            $result = array_map(function (array $x) use ($extra): array { return array_merge($x, $extra); }, $result);
        }

        $this->data = $result;
    }

    public function string2Array(array $extra = null) {
        if (empty($this->data)) {
            return;
        }

        $result = array();
        foreach($this->data as $value) {
            $result[] = array('', array_pop($value));
        }
        if ($extra !== null) {
            $result = array_map($result, function (array $x) use ($extra): array { return array_merge($x, $extra); });
        }

        $this->data = $result;
    }

    public function toArray(): array {
        if ($this->type === self::EMPTY) {
            return array();
        } else {
            return $this->data;
        }
    }

    public function toString(): string {
        return (string) ($this->data[0] ?? '');
    }

    public function toInt(): int {
        if ($this->data === null) {
            return 0;
        }

        return (int) $this->data[0];
    }

    public function toUuid(): string {
        if ($this->data === null) {
            return '';
        }

        return (string) $this->data[0];
//        return (string) '"'.$this->data[0].'"';
    }

    public function isType($type): bool {
        return $this->type === $type;
    }

    public function offsetExists($offset): bool {
        return isset($this->data[$offset]);
    }

    #[\ReturnTypeWillChange]
    public function offsetGet($offset) {
        return $this->data[$offset];
    }

    public function offsetSet($offset, $value): void {
        // Nothing. No update on that result

    }

    public function offsetUnset($offset): void {
        // Nothing. No update on that result

    }

    public function rewind(): void {
        if ($this->type === self::ARRAY) {
            reset($this->data);
        }
    }

    #[\ReturnTypeWillChange]
    public function current() {
        return current($this->data);
    }

    #[\ReturnTypeWillChange]
    public function key() {
        if ($this->type === self::ARRAY) {
            return key($this->data);
        }

        return '';
    }

    public function next(): void {
        next($this->data);
    }

    public function valid(): bool {
        if ($this->type === self::ARRAY) {
            return key($this->data) !== null;
        }

        return false;
    }

    public function count(): int {
        if ($this->type === self::ARRAY) {
            return count($this->data);
        }
        return 0;
    }
}

?>
<?php declare(strict_types = 1);

namespace Exakat\Graph\Helpers;

use Brightzone\GremlinDriver\Serializers\SerializerInterface;
use Brightzone\GremlinDriver\InternalException;
use Brightzone\GremlinDriver\RequestMessage;

/**
 * Gremlin-server PHP JSON Serializer class
 * Builds and parses message body for Messages class
 *
 * @category DB
 * @package  Serializers
 * @author   Dylan Millikin <dylan.millikin@brightzone.fr>
 * @author   Damien Seguy <dseguy@exakat.io>
 * @license  http://www.apache.org/licenses/LICENSE-2.0 apache2
 */
class GraphsonV3 implements SerializerInterface
{
    /**
     * @var string the name of the serializer
     */
    public static $name = 'GRAPHSON3';

    /**
     * @var int Value of this serializer. Will be deprecated in TP3
     */
    public static $mimeType = 'application/json';

    /**
     * @var array The native supported types that the serializer can convert to graphson
     */
    protected static $supportedFromTypes = array(
        'string'           => 'convertString',
        'boolean'          => 'convertBoolean',
        'double'           => 'convertDouble',
        'integer'          => 'convertInteger',
        'array'            => 'convertArray',
        'object'           => 'convertObject',
        'NULL'             => 'convertNull',
//        'bitsy:VertexBean' => 'convertString',
    );

    /**
     * @var array The GraphSON supported types that the serializer can deconvert from
     */
    protected static $supportedGSTypes = array(
        'g:Int32',
        'g:Int64',
        'g:Date',
        'g:Timestamp',
        'g:UUID',
        'g:Float',
        'g:Double',
        'g:List',
        'g:Map',
        'g:Set',
        'g:Class',
        'g:Path',
        'g:Tree',
        'g:Vertex',
        'g:VertexProperty',
        'tinker:graph',
        'g:Edge',
        'g:Property',
        'g:T',
        'gx:BigDecimal',
        'bitsy:VertexBean',
        'bitsy:UUID',
    );

    /**
     * Serializes the data
     *
     * @param array &$data data to be serialized
     *
     * @return int length of generated string
     * @throws InternalException
     */
    public function serialize(&$data)
    {
        //convert the array into the correct format
        $data = $this->convert($data);
        $jsonEncoder = new Json();

        return $jsonEncoder->serialize($data);
    }

    /**
     * Unserializes the data
     *
     * @param mixed $data data to be unserialized
     *
     * @return array unserialized message
     * @throws InternalException
     */
    public function unserialize($data)
    {
        $jsonEncoder = new Json();
        $data = $jsonEncoder->unserialize($data);

        return $this->deconvert($data);
    }

    /**
     * Get this serializer's Name
     *
     * @return string name of serializer
     */
    public function getName()
    {
        return self::$name;
    }

    /**
     * Get this serializer's value
     * This will be deprecated with TP3 Gremlin-server
     *
     * @return string name of serializer
     */
    public function getMimeType()
    {
        return self::$mimeType;
    }

    /**
     * Transforms a variable into it's graphson 3.0 counterpart structure
     *
     * @param mixed $item the variable to convert to the graphson 3 structure
     *
     * @return array|string The same element in it's new form.
     * @throws InternalException
     */
    public function convert($item)
    {
        $converted = array();
        $type = gettype($item);

        if(isset(self::$supportedFromTypes[$type]))
        {
            //use the type name to run the proper method
            $method = self::$supportedFromTypes[$type];
            $converted = $this->$method($item);
        }
        else
        {
            print "Item type '{$type}' is not currently supported by the serializer (" . static::class . ')' . PHP_EOL;
            $converted = '';
        }

        return $converted;
    }

    /**
     * Convert a string into it's graphson 3.0 form
     *
     * @param string $string The string to convert
     *
     * @return string converted string (same as original currently)
     */
    public function convertString($string)
    {
        return $string;
    }

    public function convertbitsy_VertexBean($string)
    {
        return $string;
    }
    /**
     * Convert an integer into it's graphson 3.0 form
     *
     * @param int $int The integer to convert
     *
     * @return array converted integer
     */
    public function convertInteger($int)
    {
        $intSize = array(
            4 => 'g:Int32',
            8 => 'g:Int64',
        );

        return array(
            '@type'  => $intSize[PHP_INT_SIZE],
            '@value' => $int,
        );
    }

    /**
     * Convert a NULL into it's graphson 3.0 form
     *
     * @param null $null The NULL to convert
     *
     * @return null converted NULL
     */
    public function convertNULL($null)
    {
        return $null;
    }

    /**
     * Convert an float/double into it's graphson 3.0 form
     *
     * @param double $double The float/double to convert
     *
     * @return array converted double
     */
    public function convertDouble($double)
    {
        return array(
            '@type'  => 'g:Double',
            '@value' => $double,
        );
    }

    /**
     * Convert a boolean into it's graphson 3.0 form
     *
     * @param bool $bool The boolean to convert
     *
     * @return bool converted boolean (same as original)
     */
    public function convertBoolean($bool)
    {
        return $bool;
    }

    /**
     * Convert an array into it's corresponding graphson 3.0 form(List or Map)
     * This differentiates between Maps and Lists (we do not convert to Set)
     *
     * @param array $array The array to convert
     *
     * @return array converted array
     * @throws InternalException
     */
    public function convertArray($array)
    {
        $isList = (empty($array) || array_keys($array) === range(0, count($array) - 1));

        return $isList ? $this->convertList($array) : $this->convertMap($array);
    }

    /**
     * Convert an array into a graphson 3.0 List
     *
     * @param array $array The array to convert
     *
     * @return array converted to GS3 List
     * @throws InternalException
     */
    public function convertList($array)
    {
        $converted = array(
            '@type'  => 'g:List',
            '@value' => array(),
        );

        foreach($array as $value)
        {
            $converted['@value'][] = $this->convert($value);
        }

        return $converted;
    }

    /**
     * Convert an array into a graphson 3.0 Map
     *
     * @param array $array The array to convert
     *
     * @return array converted to GS3 Map
     * @throws InternalException
     */
    public function convertMap($array)
    {
        $converted = array(
            '@type'  => 'g:Map',
            '@value' => array(),
        );
        foreach($array as $key => $value)
        {
            $converted['@value'][] = $this->convert($key);
            $converted['@value'][] = $this->convert($value);
        }

        return $converted;
    }

    /**
     * Convert an object into a graphson 3.0 Map
     * Currently unsuported
     *
     * @param object $object The array to convert
     *
     * @return array the converted object
     * @throws InternalException
     */
    public function convertObject($object)
    {
        if($object instanceof RequestMessage)
        {
            $converted = array();
            foreach($object->jsonSerialize() as $key => $value)
            {
                $converted[$key] = $this->convert($value);
            }

            return $converted;
        }
        else
        {
            throw new InternalException("Objects other than RequestMessage aren't currently supported by the " . self::$name . ' serializer (' . static::class . '). Error produced by: ' . get_class($object), 500);
        }
    }

    /**
     * Transforms a graphson 3.0 "variable" into it's native structure
     *
     * @param mixed $item the variable to convert to php native
     *
     * @return mixed The same element in it's new form.
     * @throws InternalException
     */
    public function deconvert($item)
    {
        $deconverted = array();

        if(is_array($item) && isset($item['@type']) && in_array($item['@type'], self::$supportedGSTypes))
        {
            //type exists in array and is found in our supported types
            $method = 'deconvert' . ucfirst(str_replace(array('g:', 'gx:', 'bitsy:',  ':'), '', $item['@type']));
            $deconverted = $this->$method($item['@value']);
        }
        elseif(is_array($item) && isset($item['@type']) && !in_array($item['@type'], self::$supportedGSTypes))
        {
            //type exists in array but is not currently supported
            print "Item type '{$item['@type']}' is not currently supported by the serializer (" . static::class . ')' . PHP_EOL;
            $deconverted = array();
        }
        elseif(is_array($item) && !isset($item['@type']))
        {
            //regular array, just pass it along
            foreach($item as $key => $value)
            {
                $deconverted[$key] = $this->deconvert($value);
            }
        }
        else
        {
            //regular variable, just pass it along.
            $deconverted = $item;
        }

        return $deconverted;
    }

    /**
     * Deconvert an Int32 into it's native form
     *
     * @param int $int The int to convert
     *
     * @return int deconverted Int32
     */
    public function deconvertBigDecimal($int)
    {
        return $int;
    }

    /**
     * Deconvert a bitsy:vertexBean. Basically, a string, but may be a UUID
     */
    public function deconvertVertexBean($string)
    {
        return $string;
    }

    /**
     * Deconvert an Int32 into it's native form
     *
     * @param int $int The int to convert
     *
     * @return int deconverted Int32
     */
    public function deconvertInt32($int)
    {
        return $int;
    }

    /**
     * Deconvert an Int64 into it's native form
     *
     * @param int $int The int to convert
     *
     * @return int deconverted Int64
     * @throws InternalException
     */
    public function deconvertInt64($int)
    {
        if(PHP_INT_SIZE == 4)
        {
            throw new InternalException('You are running a 32bit PHP and cannot convert the 64bit Int provided in the GraphSON 3.0');
        }

        return $int;
    }

    /**
     * Deconvert a Double into it's native form
     *
     * @param double $double The double to convert
     *
     * @return double deconverted Double
     */
    public function deconvertDouble($double)
    {
        return $double;
    }

    /**
     * Deconvert a Float into it's native form
     *
     * @param double $float The float to convert
     *
     * @return double deconverted Float
     */
    public function deconvertFloat($float)
    {
        return $this->deconvertDouble($float);
    }

    /**
     * Deconvert a Timestamp into it's native form
     *
     * @param int $timestamp The Timestamp to convert
     *
     * @return int deconverted Timestamp
     */
    public function deconvertTimestamp($timestamp)
    {
        return $this->deconvertInt32($timestamp);
    }

    /**
     * Deconvert a Date into it's native form
     *
     * @param int $date The date to convert
     *
     * @return int deconverted Date
     */
    public function deconvertDate($date)
    {
        return $this->deconvertInt32($date);
    }

    /**
     * Deconvert a UUID into it's native form
     *
     * @param string $uuid The UUID to convert
     *
     * @return string deconverted UUID
     */
    public function deconvertUUID($uuid)
    {
        return $uuid;
    }

    /**
     * Deconvert a List into it's native form (Array)
     *
     * @param array $list The List to convert
     *
     * @return array deconverted List
     * @throws InternalException
     */
    public function deconvertList($list)
    {
        $deconverted = array();
        foreach($list as $value)
        {
            $deconverted[] = $this->deconvert($value);
        }

        return $deconverted;
    }

    /**
     * Deconvert a Set into it's native form (Array)
     *
     * @param array $set The Set to convert
     *
     * @return array deconverted Set
     * @throws InternalException
     */
    public function deconvertSet($set)
    {
        return $this->deconvertList($set);
    }

    /**
     * Deconvert a Map into it's native form (Array)
     *
     * @param array $map The Map to convert
     *
     * @return array deconverted Map
     * @throws InternalException
     */
    public function deconvertMap($map)
    {
        if (empty($map)) {
            return array();
        }

        $deconverted = array();

        if(count($map) % 2 != 0)
        {
            throw new InternalException('Failed to deconvert Map item from graphson 3.0. Odd number of elements found (should be even)', 500);
        }

        $i = 0;
        while(0 < count($map))
        {
            $key = $this->deconvert(array_shift($map));
            $value = $this->deconvert(array_shift($map));

            if(!is_numeric($key) && !is_string($key))
            {
                throw new InternalException('Failed to deconvert Map item from graphson 3.0. A key was of type [' . gettype($key) . '], only Integers and Strings are supported', 500);
            }
            $deconverted[$key] = $value;

            ++$i;
        }

        return $deconverted;
    }

    /**
     * Deconvert a Property into it's native form
     *
     * @param array $prop The Property to convert
     *
     * @return mixed deconverted Property
     * @throws InternalException
     */
    public function deconvertProperty($prop)
    {
        return $this->deconvert($prop);
    }

    /**
     * Deconvert a VertexProperty into it's native form
     *
     * @param array $vertexProp The VertexProperty to convert
     *
     * @return mixed deconverted VertexProperty
     * @throws InternalException
     */
    public function deconvertVertexProperty($vertexProp)
    {
        return $this->deconvert($vertexProp);
    }

    /**
     * Deconvert a Path into it's native form
     *
     * @param array $path The Path to convert
     *
     * @return array deconverted Path
     * @throws InternalException
     */
    public function deconvertPath($path)
    {
        return $this->deconvert($path);
    }

    /**
     * Deconvert a TinkerGraph into it's native form
     *
     * @param array $tinkergraph The TinkerGraph to convert
     *
     * @return array deconverted TinkerGraph
     * @throws InternalException
     */
    public function deconvertTinkergraph($tinkergraph)
    {
        return $this->deconvert($tinkergraph);
    }

    /**
     * Deconvert a Tree into it's native form
     *
     * @param array $tree The Tree to convert
     *
     * @return array deconverted Tree
     * @throws InternalException
     */
    public function deconvertTree($tree)
    {
        $deconvert = array();
        $result = $this->deconvert($tree);
        foreach($result as $value)
        {
            if(isset($value['key']['id']))
            {
                $deconvert[$value['key']['id']] = $value;
            }
            else
                $deconvert[] = $value;
        }

        return $deconvert;
    }

    /**
     * Deconvert a Class into it's native form
     *
     * @param string $classname The class to convert
     *
     * @return void
     * @throws InternalException
     */
    public function deconvertClass($classname)
    {
        throw new InternalException("The GraphSON 3.0 contained a Class element ({$classname}). Classes are not currently supported");
    }

    /**
     * Deconvert a Vertex into it's native form
     *
     * @param array $vertex The Vertex to convert
     *
     * @return array deconverted Vertex
     * @throws InternalException
     */
    public function deconvertVertex($vertex)
    {
        $vertex['type'] = 'vertex';

        return $this->deconvert($vertex);
    }

    /**
     * Deconvert a Edge into it's native form
     *
     * @param array $edge The Edge to convert
     *
     * @return array deconverted Edge
     * @throws InternalException
     */
    public function deconvertEdge($edge)
    {
        $edge['type'] = 'edge';

        return $this->deconvert($edge);
    }

    /**
     * Deconvert a Token (T) into it's string form
     *
     * @param string $t the token to deconvert
     *
     * @return string either "id" or "label"
     */
    public function deconvertT($t)
    {
        return $t;
    }
}

class Json implements SerializerInterface
{
    /**
     * @var string the name of the serializer
     */
    public static $name = 'JSON';

    /**
     * @var int Value of this serializer. Will be deprecated in TP3
     */
    public static $mimeType = 'application/json';

    /**
     * Serializes the data
     *
     * @param array &$data data to be serialized
     *
     * @return int length of generated string
     */
    public function serialize(&$data)
    {
        $data = json_encode($data, JSON_UNESCAPED_UNICODE);

        return mb_strlen($data, 'ISO-8859-1');
    }

    /**
     * Unserializes the data
     *
     * @param array $data data to be unserialized
     *
     * @return array unserialized message
     */
    public function unserialize($data)
    {
        $mssg = json_decode($data, true, JSON_UNESCAPED_UNICODE);

        return $mssg;
    }

    /**
     * Get this serializer's Name
     *
     * @return string name of serializer
     */
    public function getName()
    {
        return self::$name;
    }

    /**
     * Get this serializer's value
     * This will be deprecated with TP3 Gremlin-server
     *
     * @return string name of serializer
     */
    public function getMimeType()
    {
        return self::$mimeType;
    }
}
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class Filenames extends Fileset {
    private array $names   = array();

    public function __construct(string $dataDir) {
        $names = @parse_ini_file($dataDir . '/data/ignore_files.ini') ?? array();
        $this->names = array_flip($names['files']);
    }

    public function setFiles(array $files) {
        foreach($files as $file) {
            $f = basename($file);
            if (isset($this->names[mb_strtolower($f)])) {
                $this->ignored[$file] = "Ignored file ($file)";
            } else {
                $this->files[] = $file;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class All extends Fileset {
    public function __construct(string $path) {
        $d = getcwd();
        if (!file_exists($path)) {
            display( "No such file as '$path' when looking for files\n");

            return;
        }

        chdir($path);
        $this->files = rglob('.');
        $this->files = array_map(function (string $path): string { return ltrim($path, '.'); }, $this->files);
        chdir($d);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class Set extends Fileset {
    private array $filesets   = array();

    public function __construct(array $filesets) {
        foreach($filesets as $fileset) {
            $this->filesets[$fileset] = array('/a/fooA.php');
        }
    }

    public function setFiles(array $files) {
        $this->files = array_intersect(array_merge(...array_values($this->filesets)), $files);
        $this->ignored = array_diff($files, $this->files);
    }

    public function setFiles(array $files) {
        foreach($files as $file) {
            $f = basename($file);
            if (isset($this->names[mb_strtolower($f)])) {
                $this->ignored[$file] = "Ignored file ($file)";
            } else {
                $this->files[] = $file;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class IgnoreDirs extends Fileset {
    private array $ignoreDirs    = array();
    private array $includeDirs   = array();

    public function __construct(array $ignoreDirs, array $includeDirs) {
        foreach(array_filter($ignoreDirs) as $ignore) {
            if ($ignore[0] === '/') {
                $this->ignoreDirs[] = "$ignore*";
            } else {
                $this->ignoreDirs[] = "*$ignore*";
            }
        }

        foreach(array_filter($includeDirs) as $include) {
            if ($include[0] === '/') {
                $this->includeDirs[] = "$include*";
            } else {
                $this->includeDirs[] = "*$include*";
            }
        }
    }

    public function setFiles(array $files) {
        foreach($files as $file) {
            $found = false;
            foreach($this->ignoreDirs as $ignore) {
                if (fnmatch($ignore, $file)) {
                    $found = true;
                    break 1;
                }
            }

            if ($found) {
                foreach($this->includeDirs as $include) {
                    if (fnmatch($include, $file)) {
                        $found = false;
                        break 1;
                    }
                }
            }

            if ($found) {
                $this->ignored[$file] = "ignore_dirs file ($file)";
            } else {
                $this->files[] = $file;
            }
        }
    }

    public function filterFile(array $result): bool {
        $file = $result['file'];
        $this->files = array();
        $this->setFiles(array($file));

        return !empty($this->files);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class IncludeDirs extends Fileset {
    private array $includeDirs   = array();

    public function __construct(array $includeDirs) {
        foreach(array_filter($includeDirs) as $include) {
            if ($include[0] === '/') {
                $this->includeDirs[] = "$include*";
            } else {
                $this->includeDirs[] = "*$include*";
            }
        }
    }

    public function setFiles(array $files) {
        foreach($files as $file) {
            $found = false;
            foreach($this->includeDirs as $ignore) {
                $found |= fnmatch($ignore, $file);
            }

            if ($found) {
                $this->ignored[$file] = "include_dir file ($file)";
            } else {
                $this->files[] = $file;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class Namespaces extends Fileset {
    private array $namespaces    = array();

    public function __construct(array $namespaces) {
        foreach(array_filter($namespaces) as $namespace) {
            if ($namespace[0] === '\\') {
                $this->namespaces[] = mb_strtolower("$namespace*");
            } else {
                $this->namespaces[] = mb_strtolower("*$namespace*");
            }
        }
    }

    public function setFiles(array $files) {
        // No feature here, as namespaces can only be filtered after load

        // Nothing to do, just pass the files to the next
        $this->files = $files;
        $this->ignoredFiles = array();
    }

    public function filterFile(array $result): bool {
        $namespace = $result['namespace'];

        foreach($this->namespaces as $n) {
            if (fnmatch($n, $namespace, FNM_NOESCAPE)) {
                return true;
            }
        }

        return false;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

abstract class Fileset {
    protected $filter;
    protected $files   = array();
    protected $ignored = array();

    public function addFilter($filter) {
        if ($this->filter === null) {
            $this->filter = $filter;
            $this->filter->setFiles($this->files);
        } else {
            $this->filter->addFilter($filter);
        }
    }

    public function getFiles($files = null): array {
        if ($this->filter === null) {
            return $this->files;
        } else {
            return $this->filter->getFiles();
        }
    }

    public function getIgnored(): array {
        if ($this->filter === null) {
            return $this->ignored;
        } else {
            return $this->filter->getIgnored();
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Fileset;

class FileExtensions extends Fileset {
    private array $extensions   = array();

    public function __construct(array $extensions) {
        // todo : checks this
        $this->extensions = $extensions;
    }

    public function setFiles(array $files) {
        foreach($files as $file) {
            $ext = pathinfo($file, PATHINFO_EXTENSION);
            if (in_array(mb_strtolower($ext), $this->extensions)) {
                $this->files[] = $file;
            } else {
                $this->ignored[$file] = "Ignored extension ($ext)";
            }
        }
    }

    public function filterFile(array $result): bool {
        $this->files = array();
        $this->setFiles(array($result['file']));

        return !empty($this->files);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Loader;

use Exakat\Tasks\Helpers\Atom;

abstract class Loader {
    abstract public function __construct(\Sqlite3 $sqlite, Atom $id0) ;

    abstract public function finalize(array $relicat): bool;

    public function saveFiles(string $exakatDir, array $atoms, array $links): void {}
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Loader;

use Exakat\Tasks\Helpers\Atom;

class Collector extends Loader {
    private $cit        = array();
    private $functions  = array();
    private $constants  = array();

    private $datastore  = null;

    public function __construct(\Sqlite3 $sqlite, Atom $id0) {
        $this->datastore = exakat('datastore');
    }

    public function finalize(array $relicat): bool {
        $this->datastore->addRow('ignoredCit',       $this->cit);
        $this->datastore->addRow('ignoredFunctions', $this->functions);
        $this->datastore->addRow('ignoredConstants', $this->constants);

        return true;
    }

    public function saveFiles(string $exakatDir, array $atoms, array $links): void {
        $isDefine = false;

        $lastConst = array();
        foreach($atoms as $atom) {
            if (in_array($atom->atom, array('Class', 'Interface', 'Trait'))) {
                $this->cit[] = array('name'        => $atom->fullcode,
                                     'fullnspath'  => $atom->fullnspath,
                                     'fullcode'    => $atom->fullcode,
                                     'type'        => strtolower($atom->atom),
                              );
                continue;
            }

            if (in_array($atom->atom, array('Function'))) {
                $this->functions[] = array('name'        => $atom->fullcode,
                                           'fullnspath'  => $atom->fullnspath,
                                           'fullcode'    => $atom->fullcode
                              );
                continue;
            }

            if (in_array($atom->atom, array('Identifier'))) {
                if ($isDefine === true) {
                    $this->constants[] = array('name'        => $atom->fullcode,
                                               'fullnspath'  => $atom->fullnspath,
                                               'fullcode'    => $atom->fullcode,
                                               'value'       => strtolower($atom->atom),
                                          );
                    $isDefine = false;
                } else {
                    $lastConst = array('name'        => $atom->fullcode,
                                       'fullnspath'  => $atom->fullnspath,
                                       'fullcode'    => $atom->fullcode,
                                       'value'       => strtolower($atom->atom),
                                  );
                }
                continue;
            }

            if (in_array($atom->atom, array('Constant'))) {
                if (!empty($lastConst)) {
                    $this->constants[] = $lastConst;
                }
                $lastConst = array();
                continue;
            }

            if (in_array($atom->atom, array('Defineconstant'))) {
                $isDefine = true;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Loader;

use Exakat\Data\Collector;
use Exakat\Tasks\Helpers\Atom;
use Exakat\Helpers\Timer;
use stdClass;

class SplitGraphson extends Loader {
    private const CSV_SEPARATOR = ',';
    private const LOAD_CHUNK      = 10000;
    private $load_chunk = self::LOAD_CHUNK;
    private const LOAD_CHUNK_LINK = 200000;
    private const LOAD_CHUNK_PROPERTY = 10000;

    private $tokenCounts   = array('Project' => 1);

    private $config = null;

    private $id        = 1;

    private $graphdb         = null;

    private $path            = '';
    private $pathLink        = '';
    private $pathProperties  = '';
    private $pathDef         = '';

    private $total           = 0;
    private $totalLink       = 0;
    private $totalProperties = 0;

    private $dictCode = null;

    private $sqlite   = null;

    private $log = null;

    public function __construct(\Sqlite3 $sqlite, Atom $id0) {
        $this->config         = exakat('config');
        $this->graphdb        = exakat('graphdb');

        $this->sqlite3        = $sqlite;
        $this->path           = "{$this->config->tmp_dir}/graphdb.graphson";
        $this->pathLink       = "{$this->config->tmp_dir}/graphdb.link.graphson";
        $this->pathProperties = "{$this->config->tmp_dir}/graphdb.properties.graphson";
        $this->pathDef        = "{$this->config->tmp_dir}/graphdb.def";

        $this->dictCode  = new Collector();

        $this->log = fopen($this->config->log_dir . '/loader.timing.csv', 'w+');

        $this->cleanCsv();

        $jsonText = json_encode($id0->toGraphsonLine($this->id)) . PHP_EOL;
        assert(!json_last_error(), 'Error encoding ' . $id0->atom . ' : ' . json_last_error_msg());

        file_put_contents($this->path, $jsonText, \FILE_APPEND);

        ++$this->total;
    }

    public function __destruct() {
        $this->cleanCsv();
    }

    public function finalize(array $relicat): bool {
        if ($this->total !== 0) {
            $this->saveNodes();
        }

        display("Init finalize\n");

        $totalDuration = new Timer();

        $this->saveNodeLinks();
        $this->saveProperties();

        $query = 'g.V().hasLabel("Project").id();';
        $res = $this->graphdb->query($query);
        $project_id = $res->toUuid();

        // @todo : move this to a Graph class method (it knows the project_id)
        $query = 'g.V().hasLabel("File").not(where( __.in("PROJECT"))).addE("PROJECT").from(__.V(' . $project_id . '));';
        $this->graphdb->query($query);

        $query = 'g.V().hasLabel("Virtualglobal").not(where( __.in("GLOBAL"))).addE("GLOBAL").from(__.V(' . $project_id . '));';
        $this->graphdb->query($query);

        $f = fopen('php://memory', 'r+');
        $total = 0;
        $chunk = 0;

        foreach($relicat as $row) {
            fputcsv($f, $row);
            ++$total;
            ++$chunk;
        }
        if ($chunk > $this->load_chunk) {
            $f = $this->saveLinks($f);
            $chunk = 0;
        }

        // global variables to global variabldefintiion
        $query = <<<'GREMLIN'
g.V().hasLabel("Virtualglobal").as('id').as('code').select('id', 'code').by(id).by('code');
GREMLIN;
        $res = $this->graphdb->query($query);
        $vg = array();
        foreach($res->toArray() as $atom) {
            $vg[$atom['code']] = $atom['id'];
        }

        // global variables to variabldefinition
        $query = <<<'GREMLIN'
g.V().hasLabel("File").out("DEFINITION").hasLabel("Variabledefinition").as('id').as('code').select('id', 'code').by(id).by('code');
GREMLIN;
       $res = $this->graphdb->query($query);
       $ids = array();
       $total = 0;
       foreach($res->toArray() as $atom) {
           if (!isset($vg[$atom['code']])) {
               continue;
           }

           if (isset($ids[$vg[$atom['code']]])) {
               $ids[$vg[$atom['code']]][] = array($vg[$atom['code']], $atom['id']);
           } else {
               $ids[$vg[$atom['code']]] = array(array($vg[$atom['code']], $atom['id']));
           }
           ++$total;
       }

       // global variables to variabldefinition
       $query = <<<'GREMLIN'
g.V().hasLabel("Global").out("GLOBAL").hasLabel("Variabledefinition").as('id').as('code').select('id', 'code').by(id).by('code');
GREMLIN;
        $res = $this->graphdb->query($query);
        foreach($res->toArray() as $atom) {
            if (!isset($vg[$atom['code']])) {
                continue;
            }

            if (isset($ids[$vg[$atom['code']]])) {
                $ids[$vg[$atom['code']]][] = array($vg[$atom['code']], $atom['id']);
            } else {
                $ids[$vg[$atom['code']]] = array(array($vg[$atom['code']], $atom['id']));
            }
            ++$total;
        }

        $timer = new Timer();
        $query = <<<'GREMLIN'
globalVars.each { y ->
    g.V(y[0]).addE('DEFINITION').to(g.V(y[1])).iterate();
}
GREMLIN;
        $this->graphdb->query($query, array('globalVars' => array_merge(...$ids) ) );
        $timer->end();

        $this->log("globals\t$total\t" . $timer->duration());

        // $GLOBALS
        $query = <<<'GREMLIN'
g.V().hasLabel("Phpvariable").has("fullcode", "\$GLOBALS").as("a")
 .V().hasLabel("Virtualglobal").as("b").as("c")
 .select("a","b").by("code")
 .where("a", eq("b")).select("c").addE("DEFINITION").to("a").count()
GREMLIN;
        $this->graphdb->query($query);
        $res = $this->graphdb->query($query);

        // global variables to $GLOBALS['x']
        $query = <<<'GREMLIN'
g.V().hasLabel("Phpvariable").has("fullcode", "\$GLOBALS").in("VARIABLE").hasLabel("Array").has("globalvar").not(where(__.in("DEFINITION"))).as("a")
 .V().hasLabel("Virtualglobal").as("b").as("c")
 .select("a","b").by("globalvar").by("code")
 .where("a", eq("b")).select("c").addE("DEFINITION").to("a").count()
GREMLIN;
        $res = $this->graphdb->query($query);
        $res->toInt() . " \$GLOBALS['d']\n";

        $definitionSQL = <<<'SQL'
SELECT DISTINCT CASE WHEN definitions.id IS NULL THEN definitions2.id ELSE definitions.id END AS definition, GROUP_CONCAT(DISTINCT calls.id) AS call, count(calls.id) AS id
FROM calls
LEFT JOIN definitions 
    ON definitions.type       = calls.type       AND
       definitions.fullnspath = calls.fullnspath
LEFT JOIN definitions definitions2
    ON definitions2.type       = calls.type       AND
       definitions2.fullnspath = calls.globalpath 
WHERE (definitions.id IS NOT NULL OR definitions2.id IS NOT NULL) AND
        CASE WHEN definitions.id IS NULL THEN definitions2.id ELSE definitions.id END != calls.id
GROUP BY definition
SQL;
        $res = $this->sqlite3->query($definitionSQL);
        // Fast dump, with a write to memory first
        while($row = $res->fetchArray(\SQLITE3_NUM)) {
            // Skip reflexive definitions, which never exist.
            if ($row[0] === $row[1]) { continue; }
            $total += $row[2];
            $chunk += $row[2];
            unset($row[2]);
            $row[0] = $this->graphdb->fixId($row[0]);
            $r = explode(',', $row[1]);
            $r = array_map(array($this->graphdb, 'fixId'), $r);
            $row[1] = implode('-', $r);
            fputcsv($f, $row);

            if ($chunk > $this->load_chunk) {
                $f = $this->saveLinks($f);
                $chunk = 0;
            }
        }

        if (empty($total)) {
            display('no definitions');
        } else {
            display("loading $total definitions");
            $this->saveLinks($f);
            display("loaded $total definitions");
        }

        $this->saveTokenCounts();

        $totalDuration->end();

        display('loaded nodes (duration : ' . $totalDuration->duration(Timer::MS) . ' ms)');

        $this->cleanCsv();
        display('Cleaning CSV');

        return true;
    }

    private function saveProperties() {
        if (!file_exists($this->pathProperties)) {
            return;
        }

        if ($this->totalProperties === 0) {
            $this->log("properties\t0");

            return;
        }

        // break down property files into small chunks for processing inside 300s.
        $fp = fopen($this->pathProperties, 'r');
        $fp2 = fopen($this->pathProperties . '.tmp', 'w+');
        $j = 0;
        for($i = 0; $i < $this->totalProperties; ++$i) {
            ++$j;
            fwrite($fp2, fgets($fp));
            if ($j >= self::LOAD_CHUNK_PROPERTY) {
                $this->savePropertiesGremlin(intdiv($i, self::LOAD_CHUNK_PROPERTY));
                fclose($fp2);
                $fp2 = fopen($this->pathProperties . '.tmp', 'w+');
                $j = 0;
            }
        }
        fclose($fp);
        fclose($fp2);
        $this->savePropertiesGremlin(intdiv($i, self::LOAD_CHUNK_PROPERTY));

        unlink($this->pathProperties);
        unlink($this->pathProperties . '.tmp');
    }

    private function savePropertiesGremlin(int $id): void {
        $timer = new Timer();
        $query = <<<GREMLIN
new File('{$this->pathProperties}.tmp').eachLine {
    (property, targets) = it.split('-');
    vertices = targets.split(',');

    g.V(vertices).property(property, true).iterate();
}

g.V().has('intval', 0).not(has("boolean", true)).property('boolean', false).iterate();

GREMLIN;
        $this->graphdb->query($query);
        $timer->end();

        $this->log("properties\t$id\t" . $timer->duration());
    }

    private function saveLinks($f) {
        rewind($f);
        $fp = fopen($this->pathDef, 'w+');
        $length = fwrite($fp, stream_get_contents($f));
        fclose($fp);
        fclose($f);

        if ($length > 0) {
            $timer = new Timer();
            $query = <<<GREMLIN
new File('$this->pathDef').eachLine {
    (fromVertex, target) = it.split(',')

    toVertices = target.split('-');

    g.V(toVertices).addE('DEFINITION').from(V(fromVertex)).iterate();
}

GREMLIN;
            $this->graphdb->query($query);
            $timer->end();

            $this->log("links finalize\t" . $timer->duration());
        }

        return fopen('php://memory', 'r+');
    }

    private function cleanCsv(): void {
        if (file_exists($this->path)) {
            unlink($this->path);
        }

        if (file_exists($this->pathLink)) {
            unlink($this->pathLink);
        }

        if (file_exists($this->pathProperties)) {
            unlink($this->pathProperties);
        }

        if (file_exists($this->pathDef)) {
            unlink($this->pathDef);
        }
    }

    private function saveTokenCounts(): void {
        $datastore = exakat('datastore');

        $datastore->addRow('tokenCounts', $this->tokenCounts);
    }

    public function saveFiles(string $exakatDir, array $atoms, array $links): void {
        $fileName = 'unknown';

        $json     = array();
        $properties = array('noscream'     => array(),
                            'reference'    => array(),
                            'variadic'     => array(),
                            'heredoc'      => array(),
                            'flexible'     => array(),
                            'constant'     => array(),
                            'enclosing'    => array(),
                            'final'        => array(),
                            'boolean'      => array(),
                            'bracket'      => array(),
                            'close_tag'    => array(),
                            'trailing'     => array(),
                            'alternative'  => array(),
                            'absolute'     => array(),
                            'abstract'     => array(),
                            'readonly'     => array(),
                            'aliased'      => array(),
                            'isRead'       => array(),
                            'isModified'   => array(),
                            'static'       => array(),
                            'isNull'       => array(),
                            );
        foreach($atoms as $atom) {
            if ($atom->atom === 'File') {
                $fileName = $atom->code;
            }
            $json[$atom->id] = $atom->toGraphsonLine($this->id);
            $fixedId = $this->graphdb->fixId($atom->id);
            foreach($atom->boolProperties() as $property) {
                $properties[$property][] = $fixedId;
            }
        }

        foreach($links as &$link) {
            $this->tokenCounts[$link[0]] = ($this->tokenCounts[$link[0]] ?? 0) + 1;

            $link[1] = $this->graphdb->fixId($link[1]);
            $link[2] = $this->graphdb->fixId($link[2]);
            $link = implode('-', $link);
        }
        unset($link);

        $total = 0; // local total
        $append = array();
        foreach($json as $j) {
            $V = $j->properties['code'][0]->value;
            $j->properties['code'][0]->value = $this->dictCode->get($V);

            if (isset($j->properties['lccode'][0]->value)) {
                $j->properties['lccode'][0]->value = $this->dictCode->get($j->properties['lccode'][0]->value);
            }

            if (isset($j->properties['propertyname']) ) {
                $j->properties['propertyname'][0]->value = $this->dictCode->get($j->properties['propertyname'][0]->value);
            }

            if (isset($j->properties['globalvar']) ) {
                $j->properties['globalvar'][0]->value = $this->dictCode->get($j->properties['globalvar'][0]->value);
            }

            $X = $this->json_encode($j);
            assert(!json_last_error(), $fileName . ' : error encoding normal ' . $j->label . ' : ' . json_last_error_msg() . "\n" . print_r($j, true));
            $append[] = $X;

            if (isset($this->tokenCounts[$j->label])) {
                ++$this->tokenCounts[$j->label];
            } else {
                $this->tokenCounts[$j->label] = 1;
            }
            ++$this->total;

            if ($this->total > $this->load_chunk) {
                file_put_contents($this->path, implode(PHP_EOL, $append) . PHP_EOL, \FILE_APPEND);
                $this->saveNodes();
                $append = array();
            }

            ++$total;
        }

        if (!empty($append)) {
            file_put_contents($this->path, implode(PHP_EOL, $append) . PHP_EOL, \FILE_APPEND);
        }
        $this->totalLink += count($links);
        file_put_contents($this->pathLink, implode(PHP_EOL, $links) . PHP_EOL, \FILE_APPEND);
        foreach($properties as $property => $targets) {
            if (!empty($targets)) {
                $chunks = array_chunk($targets, self::LOAD_CHUNK_PROPERTY);
                foreach($chunks as $chunk) {
                    ++$this->totalProperties;
                    file_put_contents($this->pathProperties, $property . '-' . implode(',', $chunk) . PHP_EOL, \FILE_APPEND);
                }
            }
        }

        if ($this->total > $this->load_chunk) {
            $this->saveNodes();
        }

        $datastore = exakat('datastore');
        $datastore->addRow('dictionary', $this->dictCode->getRecent());
    }

    private function saveNodes(): void {
        $size = filesize($this->path);
        if ($size === 0) {
            return;
        }

        $timer = new Timer();
        $this->graphdb->query("graph.io(IoCore.graphson()).readGraph(\"$this->path\");");
        unlink($this->path);
        $timer->end();
        $this->log("path\t{$this->total}\t$size\t" . $timer->duration());

        $this->total = 0;
    }

    private function saveNodeLinks(): void {
        if (!file_exists($this->pathLink)) {
            return;
        }

        if ($this->totalLink === 0) {
            return ;
        }

        // break down property files into small chunks for processing inside 300s.
        $fp = fopen($this->pathLink, 'r');
        $fp2 = fopen($this->pathLink . '.tmp', 'w+');
        $j = 0;
        for($i = 0; $i < $this->totalLink; ++$i) {
            ++$j;
            fwrite($fp2, fgets($fp));
            if ($j >= self::LOAD_CHUNK_LINK) {
                $this->saveLinkGremlin(intdiv($i, self::LOAD_CHUNK_LINK));
                fclose($fp2);
                $fp2 = fopen($this->pathLink . '.tmp', 'w+');
                $j = 0;
            }
        }
        fclose($fp);
        fclose($fp2);
        $this->saveLinkGremlin(intdiv($i, self::LOAD_CHUNK_LINK));

        unlink($this->pathLink);
        unlink($this->pathLink . '.tmp');
    }

    private function saveLinkGremlin(int $id): void {
        $timer = new Timer();

        $query = <<<GREMLIN
new File('{$this->pathLink}.tmp').eachLine {
    (theLabel, fromVertex, toVertex) = it.split('-');

    g.V(fromVertex).addE(theLabel).to(V(toVertex)).iterate();
}

GREMLIN;
        $this->graphdb->query($query);
        $timer->end();

        $this->log("links\t$id\t" . $timer->duration());
    }

    private function json_encode(Stdclass $object): string {
        // in case the function name is full of non-encodable characters.
        if (isset($object->properties['fullnspath']) && !mb_check_encoding($object->properties['fullnspath'][0]->value, 'UTF-8')) {
            $object->properties['fullnspath'][0]->value = utf8_encode($object->properties['fullnspath'][0]->value);
        }
        if (isset($object->properties['propertyname']) && !mb_check_encoding((string) $object->properties['propertyname'][0]->value, 'UTF-8')) {
            $object->properties['propertyname'][0]->value = utf8_encode($object->properties['propertyname'][0]->value);
        }
        if (isset($object->properties['fullcode']) && !mb_check_encoding((string) $object->properties['fullcode'][0]->value, 'UTF-8')) {
            $object->properties['fullcode'][0]->value = utf8_encode($object->properties['fullcode'][0]->value);
        }
        if (isset($object->properties['code']) && !mb_check_encoding((string) $object->properties['code'][0]->value, 'UTF-8')) {
            $object->properties['code'][0]->value = utf8_encode($object->properties['code'][0]->value);
        }
        if (isset($object->properties['noDelimiter']) && !mb_check_encoding((string) $object->properties['noDelimiter'][0]->value, 'UTF-8')) {
            $object->properties['noDelimiter'][0]->value = utf8_encode($object->properties['noDelimiter'][0]->value);
        }
        if (isset($object->properties['delimiter']) && !mb_check_encoding((string) $object->properties['delimiter'][0]->value, 'UTF-8')) {
            $object->properties['delimiter'][0]->value = utf8_encode($object->properties['delimiter'][0]->value);
        }
        if (isset($object->properties['globalvar']) && !mb_check_encoding((string) $object->properties['globalvar'][0]->value, 'UTF-8')) {
            $object->properties['globalvar'][0]->value = utf8_encode($object->properties['globalvar'][0]->value);
        }
        if (isset($object->properties['ws']) && !mb_check_encoding((string) $object->properties['ws'][0]->value, 'UTF-8')) {
            $object->properties['ws'][0]->value = utf8_encode($object->properties['ws'][0]->value);
        }

        return json_encode($object);
    }

    private function log(string $message): void {
        fwrite($this->log, $message . PHP_EOL);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Loader;

use Exakat\Tasks\Helpers\Atom;
use Sqlite3;

class None extends Loader {
    public function __construct(Sqlite3 $sqlite, Atom $id0) {}

    public function finalize(array $relicat): bool {
        return true;
    }


}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class Mercurial extends Vcs {
    private $executable = 'hg';

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version 2>&1");
        if (strpos($res, 'Mercurial') === false) {
            throw new HelperException('Mercurial');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $sourceArg = escapeshellarg($source);
        $codePath = dirname($this->destinationFull);
        $this->shell("cd {$codePath}; {$this->executable} clone $sourceArg code");
    }

    public function update() {
        $this->check();

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} pull 2>&1; {$this->executable} update; {$this->executable} log -l 1");
        preg_match('/changeset:\s+(\S+)/', $res, $changeset);
        preg_match("/date:\s+([^\n]+)/", $res, $date);

        return "$changeset[1] ($date[1])";
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = trim($this->shell($this->executable . ' --version 2>&1'));
        if (preg_match('/Mercurial Distributed SCM \(version ([0-9\.]+)\)/', $res, $r)) {//
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['installed'] = 'No';
            $stats['optional'] = 'Yes';
        }

        return $stats;
    }

    public function getBranch(): string {
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} summary 2>&1 | grep branch");
        return trim(substr($res, 8), " *\n");
    }

    public function getRevision() {
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} summary 2>&1 | grep parent");
        return trim(substr($res, 8), " *\n");
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'hg',
                        'branch'    => $this->getBranch(),
                        'revision'  => $this->getRevision(),
                        'updatable' => true
                       );

        return $status;
    }

    public function getDiffLines($r1, $r2): array {
        display("No support for line diff in Hg.\n");
        return array();
    }

    public function getLastCommitDate(): int {
        $res = trim($this->shell("cd {$this->destinationFull}; {$this->executable} log -l 1 2>&1"));

        //date:        Wed Jun 23 11:19:15 2010 -0700
        if (preg_match('/date:\s+(\S.+\d{4})/m', $res, $r)) {
            return strtotime($r[1]);
        } else {
            return 0;
        }
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class Bazaar extends Vcs {
    private $executable = 'bzr';

    public function __construct($destination, $project_root) {
        parent::__construct($destination, $project_root);
    }

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version 2>&1");
        if (strpos($res, 'Bazaar') === false) {
            throw new HelperException('Bazar');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $source = escapeshellarg($source);
        $this->shell("cd {$this->destinationFull}; {$this->executable} branch $source code");
    }

    public function update() {
        $this->check();

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} update 2>&1");
        if (preg_match('/revision (\d+)/', $res, $r)) {
            return $r[1];
        } else {
            return '';
        }
    }

    public function getBranch(): string {
        $this->check();

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} version-info 2>&1 | grep branch-nick");
        return trim(substr($res, 13), " *\n");
    }

    public function getRevision() {
        $this->check();

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} version-info 2>&1 | grep revno");
        return trim(substr($res, 7), " *\n");
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = trim($this->shell("{$this->executable} --version 2>&1"));
        if (preg_match('/Bazaar \(bzr\) ([0-9\.]+) /', $res, $r)) {//
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['installed'] = 'No';
            $stats['optional'] = 'Yes';
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'bzr',
                        'branch'    => $this->getBranch(),
                        'revision'  => $this->getRevision(),
                        'updatable' => true,
                       );

        return $status;
    }

    public function getDiffLines($r1, $r2): array {
        display("No support yet for line diff in Bazaar.\n");
        return array();
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;
use Exakat\Exceptions\VcsError;

class Git extends Vcs {
    private $installed  = false;
    private $version    = 'unknown';
    private $executable = 'git';

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version 2>&1");
        if (strpos($res, 'git') === false) {
            throw new HelperException('git');
        }

        if (preg_match('/git version ([0-9\.]+)/', trim($res), $r)) {
            $this->installed = true;
            $this->version   = $r[1];

            if (file_exists($this->destinationFull)) {
                $res = $this->shell("cd {$this->destinationFull}/; {$this->executable} branch | grep \\* 2>&1");
                $this->branch = substr(trim($res), 2);
            }
        } else {
            $this->installed = false;
        }
    }

    public function clone(string $source): void {
        $this->check();
        $repositoryDetails = parse_url($source);

        if (isset($repositoryDetails['user'])) {
            $repositoryDetails['user'] = urlencode($repositoryDetails['user']);
        } else {
            $repositoryDetails['user'] = '';
        }
        if (isset($repositoryDetails['pass'])) {
            $repositoryDetails['pass'] = urlencode($repositoryDetails['pass']);
        } else {
            $repositoryDetails['pass'] = '';
        }

        unset($repositoryDetails['query']);
        unset($repositoryDetails['fragment']);
        $repositoryNormalizedURL = unparse_url($repositoryDetails);

        $codePath = dirname($this->destinationFull);
        $shell = "cd $codePath;{$this->executable} clone -q ";

        if (!empty($this->branch)) {
            display("Check out with branch '$this->branch'");
            $shell .= " -b $this->branch ";
        } elseif (!empty($this->tag)) {
            display("Check out with tag '$this->tag'");
            $shell .= " -b $this->tag ";
        } else {
            display('Check out with default branch');
        }

        $shell .= $repositoryNormalizedURL . ' code 2>&1 ';
        $shellResult = $this->shell($shell);

        if (($offset = strpos($shellResult, 'fatal: ')) !== false) {
            $errorMessage = str_replace($repositoryNormalizedURL, $source, $shellResult);
            $errorMessage = trim(substr($shellResult, $offset + 7));

            throw new VcsError('Git', $errorMessage);
        }
    }

    public function update(): string {
        $this->check();

        if (!file_exists($this->destinationFull . '/.git')) {
            display("This doesn't seem to be a git repository. Aborting\n");

            return self::NO_UPDATE;
        }

        $res = $this->shell("cd {$this->destinationFull}/; {$this->executable} branch | grep \\* 2>&1");
        $branch = substr(trim($res), 2);

        if (strpos($branch, ' detached at ') === false) {
            $resInitial = $this->shell("cd {$this->destinationFull}/; {$this->executable} show-ref --heads $branch");
        } else {
            $resInitial = $this->shell("cd {$this->destinationFull}/; {$this->executable} checkout --quiet; {$this->executable} pull; {$this->executable} branch | grep '* '");
            $branch = '';
        }

        $this->shell("cd {$this->destinationFull}/;GIT_TERMINAL_PROMPT=0  {$this->executable} checkout $branch --quiet; {$this->executable} pull --quiet");

        $resFinal = $this->shell("cd {$this->destinationFull}/; {$this->executable} show-ref --heads $branch");
        if (strpos($resFinal, ' ') !== false) {
            list($resFinal) = explode(' ', $resFinal, 1);
        }

        return $resFinal;
    }

    public function setBranch(string $branch = ''): void {
        $this->branch = $branch;
    }

    public function setTag(string $tag = ''): void {
        $this->tag = $tag;
    }

    public function getBranch(): string {
        if (!file_exists("{$this->destinationFull}/")) {
            return '';
        }
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} branch | grep \* 2>&1");
        $this->branch = trim($res, " *\n");

        return $this->branch;
    }

    public function getRevision() {
        if (!file_exists($this->destinationFull)) {
            return '';
        }

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} rev-parse HEAD 2>&1");
        return trim($res);
    }

    public function getInstallationInfo() {
        $this->check();
        $stats = array('installed' => $this->installed === true ? 'Yes' : 'No',
                      );

        if ($this->installed === true) {
            $stats['version'] = $this->version;
            if (version_compare($this->version, '2.3') < 0) {
                $stats['version 2.3'] = 'It is recommended to use git version 2.3 or more recent (' . $this->version . ' detected), for security reasons and the support of GIT_TERMINAL_PROMPT';
            }
        } else {
            $stats['optional'] = 'Yes';
        }

        return $stats;
    }

    public function getStatus(): array {
        $name = $this->shell("{$this->executable} config user.name");
        $email = $this->shell("{$this->executable} config user.email");

        $status = array('vcs'       => 'git',
                        'branch'    => $this->getBranch(),
                        'revision'  => $this->getRevision(),
                        'updatable' => true,
                        'name'      => $name,
                        'email  '   => $email,
                       );

        return $status;
    }

    public function getDiffLines($r1, $r2): array {
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} diff -U0 -r $r1 -r $r2");

        $file    = '';
        $changes = array();

        $lines = explode(PHP_EOL, $res);
        foreach ($lines as $line) {
            if (preg_match('#diff --git a(/.*?) b(/.*)#', $line, $r)) {
                $file = $r[1];
                continue;
            }

            if (preg_match('#@@ \-(\d+)(,(\d+))? \+(\d+)(,(\d+))?( )@@#', $line, $r, PREG_UNMATCHED_AS_NULL)) {
                $c = ($r[6] ?? 1) - ($r[3] ?? 1);
                if ($c !== 0) {
                    $changes[] = array('file' => $file,
                                       'line' => $r[1],
                                       'diff' => $c,
                                       );
                }
            }
        }

        return $changes;
    }

    public function getFileModificationLoad(): array {
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} log --name-only --pretty=format:");

        $files = array();
        $rows = explode(PHP_EOL, $res);
        foreach ($rows as $row) {
            if (empty($row)) {
                continue;
            }
            if (isset($files[$row])) {
                ++$files[$row];
            } else {
                $files[$row] = 1 ;
            }
        }

        return $files;
    }

    public function getDiffFile(string $next): array {
        // Added and removed ?
         $res = $this->shell("cd {$this->destinationFull}; {$this->executable} diff --diff-filter=a --name-only $next -- . ");

        if (empty($res)) {
            return array();
        }

        $return = explode("\n", trim($res));
        $return = array_map(function ($x) { return "/$x"; }, $return);

         return $return;
    }

    public function checkOut($next) {
        //--diff-filter=[(A|C|D|M|R|T|U|X|B)…​[*]]
        // Some situations are not supported yet.
        // We keep Added, Modified. Deleted are ignored, as non-treatable.
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} diff --diff-filter=d --name-only $next -- . ");

        // No chane, may be, but we still need to update the code
        $this->shell("cd {$this->destinationFull}; {$this->executable} checkout $next");

        if (empty($res)) {
            return array();
        }

        $return = explode("\n", trim($res));
        $return = array_map(function ($x) { return "/$x"; }, $return);

        return $return;
    }

    public function getLastCommitDate(): int {
         return (int) strtotime(trim($this->shell("cd {$this->destinationFull}; {$this->executable} log -1 --format=%cd")));
    }

    public function hasBranch(string $branch = ''): bool {
        $branch = escapeshellarg($branch);
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} branch --list $branch");

        // fatal: A branch named 'freezy-sk-fix-typo' already exists.
        return !empty($res);
    }

    public function createBranch(string $branch = ''): bool {
        $branch = escapeshellarg($branch);
        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} checkout -q -b $branch");

        // fatal: A branch named 'freezy-sk-fix-typo' already exists.
        return strpos($res, 'fatal:') !== 0;
    }

    public function checkoutBranch(string $branch = ''): bool {
        if (empty($branch)) {
            // go back to the last branch
            $res = $this->shell("cd {$this->destinationFull}; {$this->executable} checkout -q " . $this->branch);
        } else {
            $branch = escapeshellarg($branch);
            $res = $this->shell("cd {$this->destinationFull}; {$this->executable} checkout -q $branch");
        }

        //error: pathspec 'inexistant branch' did not match any file(s) known to git
        return strpos($res, 'error:') !== 0;
    }

    public function commitFiles(string $message = 'Exakat Cobbler created those files'): bool {
        $message = escapeshellarg($message);

        $this->shell(<<<SHELL
cd {$this->destinationFull}; 
{$this->executable} stage -A ; 
git commit -m $message
SHELL
);

        return true;
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class Svn extends Vcs {
    private $info = array();
    private $executable = 'svn';

    public function __construct($destination, $project_root) {
        parent::__construct($destination, $project_root);
    }

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version 2>&1");
        if (strpos($res, 'svn') === false) {
            throw new HelperException('SVN');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $source = escapeshellarg($source);
        $codePath = dirname($this->destinationFull);
        $this->shell("cd {$codePath}; {$this->executable} checkout --quiet $source code");
    }

    public function update() {
        $this->check();

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} update");
        if (preg_match('/Updated to revision (\d+)\./', $res, $r)) {
            return $r[1];
        }

        if (preg_match('/At revision (\d+)/', $res, $r)) {
            return $r[1];
        }

        return 'Error : ' . $res;
    }

    private function getInfo() {
        $res = trim($this->shell("cd {$this->destinationFull}; {$this->executable} info"));

        if (empty($res)) {
            $this->info['svn'] = '';

            return;
        }
        foreach (explode("\n", $res) as $info) {
            list($name, $value) = explode(': ', trim($info));
            $this->info[$name] = $value;
        }
    }

    public function getBranch(): string {
        if (empty($this->info)) {
            $this->getInfo();
        }

        return $this->info['Relative URL'] ?? 'trunk';
    }

    public function getRevision() {
        if (empty($this->info)) {
            $this->getInfo();
        }

        return $this->info['Revision'] ?? 'No Revision';
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = trim($this->shell("{$this->executable} --version 2>&1"));
        if (preg_match('/svn, version ([0-9\.]+) /', $res, $r)) {//
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['installed'] = 'No';
            $stats['optional'] = 'Yes';
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'svn',
                        'revision'  => $this->getRevision(),
                        'updatable' => false
                       );

        return $status;
    }

    public function getDiffLines($r1, $r2): array {
        display("No support for line diff in SVN.\n");
        return array();
    }

    public function getLastCommitDate(): int {
        $res = trim($this->shell("cd {$this->destinationFull}; {$this->executable} info 2>&1"));

        //Last Changed Date: 2020-07-22 09:17:27 +0200 (Wed, 22 Jul 2020)
        if (preg_match('/Last Changed Date: (\d{4}.+\d{4}) /m', $res, $r)) {
            return strtotime($r[1]);
        } else {
            return 0;
        }
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;
use Exakat\Exceptions\VcsSupport;
use Exakat\Exceptions\VcsError;

class Zip extends Vcs {
    private $executable = 'unzip';

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version  2>&1");
        if (strpos($res, 'Zip') === false) {
            throw new HelperException('zip');
        }

        if (ini_get('allow_url_fopen') != true) {
            throw new HelperException('allow_url_fopen');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $binary = file_get_contents($source);
        if (empty($binary)) {
            throw new VcsError("Error while loading zip archive : archive is empty. Aborting\n");
        }
        $archiveFile = tempnam(sys_get_temp_dir(), 'archiveZip') . '.zip';
        file_put_contents($archiveFile, $binary);

        $error = $this->shell("{$this->executable} $archiveFile -d {$this->destinationFull}");

        // folder will not be created in case of error
        if (!file_exists($this->destinationFull)) {
            throw new VcsError("Error while extracting zip archive : \"$error\". Aborting\n");
        }

        unlink($archiveFile);
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = $this->shell("{$this->executable} -v  2>&1");
        if (stripos($res, 'not found') !== false) {
            $stats['installed'] = 'No';
        } elseif (preg_match('/Zip\s+([0-9\.]+)/is', $res, $r)) {
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['error'] = $res;
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'zip',
                        'updatable' => false
                       );

        return $status;
    }

    public function createBranch(string $branch): bool {
        throw new VcsSupport('Zip', ' cannot create a new branch');

        return false;
    }

    public function checkoutBranch(string $branch = ''): bool {
        throw new VcsSupport('Zip', ' cannot checkout a branch');

        return false;
    }

    public function commitFiles(string $string): bool {
        throw new VcsSupport('Zip', ' cannot commit files');

        return false;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class SevenZ extends Vcs {
    private $executable = '7z';

    public function __construct($destination, $project_root) {
        parent::__construct($destination, $project_root);
    }

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable}  2>&1");
        if (strpos($res, '7-Zip') === false) {
            throw new HelperException('7z');
        }

        if (ini_get('allow_url_fopen') != true) {
            throw new HelperException('allow_url_fopen');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $binary = file_get_contents($source);
        $archiveFile = tempnam(sys_get_temp_dir(), 'archive7Z') . '.7z';
        file_put_contents($archiveFile, $binary);

        $this->shell("{$this->executable} x $archiveFile -oc:{$this->destinationFull}");

        unlink($archiveFile);
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = $this->shell("{$this->executable}  2>&1");
        if (stripos($res, 'not found') !== false) {
            $stats['installed'] = 'No';
        } elseif (preg_match('/p7zip Version ([0-9\.]+)/is', $res, $r)) {
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['error'] = $res;
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => '7z',
                        'updatable' => false
                       );

        return $status;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class Rar extends Vcs {
    private $executable = 'unrar';

    public function __construct($destination, $project_root) {
        parent::__construct($destination, $project_root);
    }

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} 2>&1");
        if (strpos($res, 'UNRAR') === false) {
            throw new HelperException('rar');
        }

        if (ini_get('allow_url_fopen') != true) {
            throw new HelperException('allow_url_fopen');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $binary = file_get_contents($source);
        $archiveFile = tempnam(sys_get_temp_dir(), 'archiveRar') . '.rar';
        file_put_contents($archiveFile, $binary);

        $this->shell("{$this->executable} x $archiveFile {$this->destinationFull}");

        unlink($archiveFile);
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = $this->shell("{$this->executable} 2>&1");
        if (stripos($res, 'not found') !== false) {
            $stats['installed'] = 'No';
        } elseif (preg_match('/UNRAR\s+([0-9\.]+)/is', $res, $r)) {
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['error'] = $res;
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'rar',
                        'updatable' => false
                       );

        return $status;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\NoSuchDir;

class Symlink extends Vcs {
    public function clone(string $source): void {
        $source = realpath($source);

        if (empty($source)) {
            throw new NoSuchDir();
        }

        symlink($source, $this->destinationFull);
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'symlink',
                        'updatable' => false
                       );

        return $status;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class Cvs extends Vcs {
    private $info = array();
    private $executable = 'cvs';

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version 2>&1");
        if (strpos($res, 'CVS') === false) {
            throw new HelperException('Cvs');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $source = escapeshellarg($source);
        $this->shell("cd {$this->destinationFull}; {$this->executable} checkout --quiet $source code");
    }

    public function update() {
        $this->check();

        $res = $this->shell("cd {$this->destinationFull}; {$this->executable} update");
        if (preg_match('/Updated to revision (\d+)\./', $res, $r)) {
            return $r[1];
        }

        return 'CSV updated to last revision';
    }

    private function getInfo() {
        $res = trim($this->shell("cd {$this->destinationFull}; {$this->executable} info"));

        if (empty($res)) {
            $this->info['cvs'] = '';

            return;
        }
        foreach (explode("\n", $res) as $info) {
            list($name, $value) = explode(': ', trim($info));
            $this->info[$name] = $value;
        }
    }

    public function getBranch(): string {
        return 'No branch';
    }

    public function getRevision() {
        return 'No revision';
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = trim($this->shell("{$this->executable} --version 2>&1"));
        if (preg_match('/Concurrent Versions System \(CVS\) ([0-9\.]+) /', $res, $r)) {//
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['installed'] = 'No';
            $stats['optional'] = 'Yes';
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'cvs',
                        'revision'  => $this->getRevision(),
                        'updatable' => false
                       );

        return $status;
    }

    public function getDiffLines($r1, $r2): array {
        display("No support for line diff in CVS.\n");
        return array();
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;
use Exakat\Exceptions\VcsError;
use Exakat\Exceptions\VcsSupport;

class Tarbz extends Vcs {
    private $executableTar   = 'tar';
    private $executableBzip2 = 'bzip2';

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executableTar} --version 2>&1");
        if (!preg_match('#\d+\.\d+(\.\d+)?#s', $res)) {
            throw new HelperException('Tar');
        }

        $res = $this->shell("{$this->executableBzip2} --help 2>&1");
        if (strpos($res, 'bzip2') === false) {
            throw new HelperException('bzip2');
        }

        if (ini_get('allow_url_fopen') != true) {
            throw new HelperException('allow_url_fopen');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $binary = file_get_contents($source);
        if (empty($binary)) {
            throw new VcsError("Error while loading tar.bz archive : archive is empty. Aborting\n");
        }

        $archiveFile = tempnam(sys_get_temp_dir(), 'archiveTgz') . '.tar.bz2';
        file_put_contents($archiveFile, $binary);

        $res = $this->shell("{$this->executableTar} -tjf $archiveFile 2>&1 >/dev/null");
        if (!empty($res)) {
            list($l) = explode("\n", $res, 1);

            throw new VcsError("Error while extracting tar.bz archive : \"$l\". Aborting\n");
        }

        $this->shell("mkdir {$this->destinationFull}; {$this->executableTar} -jxf $archiveFile --directory $this->destinationFull");

        unlink($archiveFile);
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = trim($this->shell("{$this->executableTar} --version 2>&1"));
        if (preg_match('/^(\w+) ([0-9\.]+) /', $res, $r)) {//
            $stats['tar'] = 'Yes';
            $stats['tar version'] = $r[0];
        } else {
            $stats['tar'] = 'No';
            $stats['tar optional'] = 'Yes';
        }

        $res = trim($this->shell("{$this->executableBzip2} --help 2>&1"));
        if (preg_match('/Version ([0-9\.]+),/', $res, $r)) {//
            $stats['bzip2'] = 'Yes';
            $stats['bzip2 version'] = $r[1];
        } else {
            $stats['bzip2'] = 'No';
            $stats['bzip2 optional'] = 'Yes';
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'tar.bz2',
                        'updatable' => false
                       );

        return $status;
    }

    public function createBranch(string $branch): bool {
        throw new VcsSupport('Zip', ' cannot create a new branch');

        return false;
    }

    public function checkoutBranch(string $branch = ''): bool {
        throw new VcsSupport('Zip', ' cannot checkout a branch');

        return false;
    }

    public function commitFiles(string $string): bool {
        throw new VcsSupport('Zip', ' cannot commit files');

        return false;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;

class Composer extends Vcs {
    private $executable = 'composer';

    public function __construct($destination, $project_root) {
        parent::__construct($destination, $project_root);
    }

    protected function selfCheck(): void {
        $res = $this->shell("{$this->executable} --version 2>&1");
        if (strpos($res, 'Composer') === false) {
            throw new HelperException('Composer');
        }
    }

    public function clone(string $source): void {
        $this->check();

        // composer install
        $composer = new \stdClass();
        $composer->{'minimum-stability'} = 'dev';
        $composer->require = new \stdClass();
        $composer->require->$source = '*';
        $json = json_encode($composer, JSON_PRETTY_PRINT);

        mkdir($this->destinationFull, 0755);
        file_put_contents("{$this->destinationFull}/composer.json", $json);
        $this->shell("cd {$this->destinationFull}; {$this->executable} -q install --ignore-platform-reqs");
    }

    public function update() {
        $this->check();

        $this->shell("cd {$this->destinationFull}; {$this->executable} -q update");

        $composerPath = "{$this->destinationFull}/composer.json";
        if (!file_exists($composerPath)) {
            return '';
        }

        $jsonText = file_get_contents($composerPath);
        if (empty($jsonText)) {
            return '';
        }
        $json = json_decode($jsonText);
        $component = array_keys( (array) $json->require)[0];

        if (!file_exists("{$this->destinationFull}/composer.lock")) {
            return '';
        }
        $jsonLockText = file_get_contents("{$this->destinationFull}/composer.lock");
        if (empty($jsonLockText)) {
            return '';
        }
        $jsonLock = json_decode($jsonLockText);

        $return = '';
        foreach ($jsonLock->packages as $package) {
            if ($package->name === $component) {
                return "{$package->source->reference} (version : {$package->version})";
            }
        }

        return '';
    }

    public function getInstallationInfo() {
        $stats = array();

        $res = trim($this->shell("{$this->executable} -V 2>&1"));
        // remove colors from shell syntax
        $res = preg_replace('/\e\[[\d;]*m/', '', $res);
        if (preg_match('/Composer version ([0-9\.a-z@_\(\)\-]+) /', $res, $r)) {//
            $stats['installed'] = 'Yes';
            $stats['version'] = $r[1];
        } else {
            $stats['installed'] = 'No';
        }

        return $stats;
    }

    public function getStatus(): array {
        $composerLockPath = "{$this->destinationFull}/composer.lock";
        if (!file_exists($composerLockPath)) {
            $status = array( 'vcs'       => 'composer',
                             'updatable' => true,
                             'hash'      => 'No composer.lock',
                             );

            return $status;
        }

        $status = array( 'vcs'       => 'composer',
                         'updatable' => true,
                         );
        $composerLock = file_get_contents($composerLockPath);

        $json = json_decode($composerLock);
        if (isset($json->hash)) {
            $status['hash'] = $json->hash;
        } else {
            $status['hash'] = 'Can\'t read hash';
        }

        return $status;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Config;

abstract class Vcs {
    public const SUPPORTED_VCS = array('git', 'svn', 'cvs', 'bzr', 'hg',
                                       'composer',
                                       'tgz', 'tbz', 'zip', 'rar', 'sevenz',
                                       'none', 'symlink', 'copy',
                                       );

    protected $destination     = '';
    protected $destinationFull = '';

    protected $branch = '';
    protected $tag    = '';

    protected $checked = false;

    public const NO_UPDATE = 'No update';

    public function __construct($destination, $code_dir) {
        $this->destination     = $destination;
        $this->destinationFull = $code_dir;
    }

    abstract public function clone(string $source): void;

    public function getDiffLines($r1, $r2): array {
        return array();
    }

    public function getName() {
        $path = explode('\\', static::class);
        return strtolower(array_pop($path));
    }

    protected function check(): bool {
        if ($this->checked === true) {
            return true;
        }

        $this->selfCheck();
        $this->checked = true;

        return true;
    }

    protected function selfCheck(): void { }

    public function getLineChanges() {
        return array();
    }

    public function update() {
        return self::NO_UPDATE;
    }

    public static function getVcs(Config $config) {
        if ($config->svn === true) {
            return Svn::class;
        } elseif ($config->hg === true) {
            return Mercurial::class;
        } elseif ($config->bzr === true) {
            return Bazaar::class;
        } elseif ($config->composer === true) {
            return Composer::class;
        } elseif ($config->symlink === true) {
            return Symlink::class;
        } elseif ($config->tbz === true) {
            return Tarbz::class;
        } elseif ($config->tgz === true) {
            return Targz::class;
        } elseif ($config->zip === true) {
            return Zip::class;
        } elseif ($config->copy === true) {
            return Copy::class;
        } elseif ($config->rar === true) {
            return Rar::class;
        } elseif ($config->sevenz === true) {
            return SevenZ::class;
        } elseif ($config->cvs === true) {
            return Cvs::class;
        } elseif ($config->none === true) {
            return None::class;
        } elseif ($config->git === true) {
            return Git::class;
        } else {
            return None::class;
        }
    }

    public function getStatus(): array {
        $status = array('updatable' => false,
                       );

        return $status;
    }

    public function setBranch(string $branch = ''): void {
        $this->branch = $branch;
    }

    public function setTag(string $tag = ''): void {
        $this->tag = $tag;
    }

    public function getFileModificationLoad(): array {
        return array();
    }

    public function getLastCommitDate(): int {
         return 0;
    }

    public function shell(string $command): string {
         return shell_exec($command) ?: '' ?? '';
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

class None extends Vcs {
    public function clone(string $source): void { }

    public function getStatus(): array {
        $status = array('vcs'       => 'none',
                        'updatable' => false
                       );

        return $status;
    }

    public function createBranch(string $branch): bool {
        return false;
    }

    public function checkoutBranch(string $branch = ''): bool {
        return false;
    }

    public function commitFiles(string $string): bool {
        return false;
    }

}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\HelperException;
use Exakat\Exceptions\VcsError;
use Exakat\Exceptions\VcsSupport;

class Targz extends Vcs {
    protected function selfCheck(): void {
        $res = $this->shell('tar --version 2>&1');
        if (!preg_match('#\d+\.\d+(\.\d+)?#s', $res)) {
            throw new HelperException('Tar');
        }

        $res = $this->shell('gzip -V 2>&1');
        if (strpos($res, 'gzip') === false) {
            throw new HelperException('gzip');
        }

        if (ini_get('allow_url_fopen') != true) {
            throw new HelperException('allow_url_fopen');
        }
    }

    public function clone(string $source): void {
        $this->check();

        $binary = file_get_contents($source);
        if (empty($binary)) {
            throw new VcsError("Error while loading tar.gz archive : archive is empty. Aborting\n");
        }

        $archiveFile = tempnam(sys_get_temp_dir(), 'archiveTgz') . '.tar.gz';
        file_put_contents($archiveFile, $binary);

        $res = $this->shell("tar -tzf $archiveFile 2>&1 >/dev/null");
        if (!empty($res)) {
            list($l) = explode("\n", $res, 1);

            throw new VcsError("Error while extracting tar.gz archive : \"$l\". Aborting\n");
        }

        $this->shell("mkdir {$this->destinationFull}; tar -zxf $archiveFile -C {$this->destinationFull}");

        unlink($archiveFile);
    }

    public function getInstallationInfo(): array {
        $stats = array();

        $res = trim($this->shell('tar --version 2>&1'));
        if (preg_match('/^(\w+) ([0-9\.]+) /', $res, $r)) {//
            $stats['tar'] = 'Yes';
            $stats['tar version'] = $r[0];
        } else {
            $stats['tar'] = 'No';
            $stats['tar optional'] = 'Yes';
        }

        $res = trim($this->shell('gzip -V 2>&1'));
        if (preg_match('/gzip (\d+),/', $res, $r)) {//
            $stats['gzip'] = 'Yes';
            $stats['gzip version'] = $r[1];
        } else {
            $stats['gzip'] = 'No';
            $stats['gzip optional'] = 'Yes';
        }

        return $stats;
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'tar.gz',
                        'updatable' => false
                       );

        return $status;
    }

    public function createBranch(string $branch): bool {
        throw new VcsSupport('Zip', ' cannot create a new branch');

        return false;
    }

    public function checkoutBranch(string $branch = ''): bool {
        throw new VcsSupport('Zip', ' cannot checkout a branch');

        return false;
    }

    public function commitFiles(string $string): bool {
        throw new VcsSupport('Zip', ' cannot commit files');

        return false;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Vcs;

use Exakat\Exceptions\NoSuchDir;

class Copy extends Vcs {
    public function clone(string $source): void {
        $source = realpath($source);
        if (empty($source)) {
            throw new NoSuchDir();
        }

        copyDir($source, $this->destinationFull);
    }

    public function getStatus(): array {
        $status = array('vcs'       => 'copy',
                        'revision'  => 'N/A',
                        'updatable' => false,
                       );

        return $status;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Stubs;

use Stdclass;

class StubJson extends Stubs implements StubsInterface {
    private $stubFile    = '';
    private $stub        = array();

    public function __construct(string $stubFile) {
        $this->stubFile = $stubFile;

        $this->stub = json_decode(file_get_contents($stubFile)) ?? new Stdclass();
    }

    public function getFile(): array {
        return array( basename($this->stubFile) );
    }

    public function getFunctionList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            $functions = array_keys((array) ($definitions->functions ?? array()));
            $functions = array_map(function (string $f) use ($namespace): string { return $namespace . $f;}, $functions);
            $return[] = $functions;
        }

        return array_merge(...$return);
    }

    public function getConstantList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            $constants = array_keys((array) ($definitions->constants ?? array()));
            $constants = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $constants);
            $return[] = $constants;
        }

        return array_merge(...$return);
    }

    public function getFunctionsArgsInterval(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            $functions = (array) ($definitions->functions ?? array());
            $f = array();
            foreach($functions as $name => $F) {
                $f[] = array('name' => $namespace . $name,
                             'args_min' => count((array) ($F->arguments ?? array())),
                             'args_max' => count((array) ($F->arguments ?? array())),
                             );
            }
            $return[] = $f;
        }
        return array_merge(...$return);
    }

    public function getInterfaceList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            $interfaces = array_keys((array) ($definitions->interfaces ?? array()));
            $interfaces = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $interfaces);
            $return[] = $interfaces;
        }

        return array_merge(...$return);
    }

    public function getTraitList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            $traits = array_keys((array) ($definitions->traits ?? array()));
            $traits = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $traits);
            $return[] = $traits;
        }

        return array_merge(...$return);
    }

    public function getClassList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            $classes = array_keys((array) ($definitions->classes ?? array()));
            $classes = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $classes);
            $return[] = $classes;
        }

        return array_merge(...$return);
    }

    public function getClassConstantList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            foreach((array) ($definitions->classes ?? array()) as $class => $body) {
                $classConstants = array_keys((array) ($body->constants ?? array()));
                if (empty($classConstants)) {
                    continue;
                }
                $classConstants = array_map(function (string $constant) use ($namespace, $class): string { return $namespace . $class . '::' . $constant;}, $classConstants);
                $return[] = $classConstants;
            }
        }

        return array_merge(...$return);
    }

    public function getClassPropertyList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            foreach((array) ($definitions->classes ?? array()) as $class => $body) {
                $classProperties = array_keys((array) ($body->properties ?? array()));
                if (empty($classProperties)) {
                    continue;
                }
                $list = $body->properties;
                $classProperties = array_filter($classProperties, function (string $property) use ($list): bool { return $list->{$property}->static === true; });
                $classProperties = array_map(function (string $property) use ($namespace, $class): string { return $namespace . $class . '::' . $property;}, $classProperties);
                $return[] = $classProperties;
            }
        }

        return array_merge(...$return);
    }

    public function getClassMethodList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            foreach((array) ($definitions->classes ?? array()) as $class => $body) {
                $classMethods = array_keys((array) ($body->methods ?? array()));
                if (empty($classMethods)) {
                    continue;
                }
                $list = $body->methods;
                $classMethods = array_filter($classMethods, function (string $method) use ($list): bool { return $list->{$method}->static === true; });
                $classMethods = array_map(function (string $method) use ($namespace, $class): string { return $namespace . $class . '::' . $method;}, $classMethods);
                $return[] = $classMethods;
            }
        }

        return array_merge(...$return);
    }


    public function getPropertyList(): array {
        $return = array(array());

        foreach($this->stub->versions as $namespace => $definitions) {
            foreach((array) ($definitions->classes ?? array()) as $class => $body) {
                $classProperties = array_keys((array) ($body->properties ?? array()));
                if (empty($classProperties)) {
                    continue;
                }
                $list = $body->properties;
                $classProperties = array_filter($classProperties, function (string $property) use ($list): bool { return $list->{$property}->static === false; });
                $classProperties = array_map(function (string $property) use ($namespace, $class): string { return $namespace . $class . '::' . $property;}, $classProperties);
                $return[] = $classProperties;
            }
        }

        return array_merge(...$return);
    }

    public function getEnumCasesList(): array {
        return array();
    }

    public function getClassStaticPropertyList(): array {
        return array();
    }

    public function getEnumList(): array {
        return array();
    }

    public function getClassStaticMethodList(): array {
        return array();
    }

    public function getInterfaceMethodsNameAndCount(): array {
        return array();
    }

    public function getMethodList(): array {
        return array();
    }

    public function getFinalClasses(): array {
        return array();
    }

    public function getFinalClassConstants(): array {
        return array();
    }

    public function getFunctionNamesList(): array {
        return array();
    }

    public function getClassMethodNamesList(): array {
        return array();
    }

    public function getNamespaceList(): array {
        return array();
    }

    public function getConstructorsArgsInterval(): array {
        return array();
    }

    public function getMethodsArgsInterval(): array {
        return array();
    }

    public function getClassImplementingList(): array {
        return array();
    }

    public function getFunctionsReferenceArgs(): array {
        return array();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Stubs;

interface StubsInterface {
    public function getFile(): array;
    public function getFunctionList(): array;
    public function getConstantList(): array;
    public function getFunctionsArgsInterval(): array;
    public function getInterfaceList(): array;
    public function getEnumList(): array;
    public function getTraitList(): array;
    public function getClassList(): array;
    public function getClassConstantList(): array;
    public function getEnumCasesList(): array;
    public function getClassPropertyList(): array;
    public function getClassStaticPropertyList(): array;
    public function getClassMethodList(): array;
    public function getClassStaticMethodList(): array;
    public function getPropertyList(): array;
    public function getMethodList(): array;
    public function getInterfaceMethodsNameAndCount(): array;
    public function getFinalClasses(): array;
    public function getFinalClassConstants(): array;
    public function getFunctionNamesList(): array;
    public function getClassMethodNamesList(): array;
    public function getNamespaceList(): array;
    public function getConstructorsArgsInterval(): array;
    public function getMethodsArgsInterval(): array;
    public function getClassImplementingList(): array;
    public function getFunctionsReferenceArgs(): array;
}<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Stubs;

class Stubs {
    private array  $stubs       = array();
    private string $stubsDir    = '';

    public function __construct(string $stubsDir   = '',
                                array $stubsFiles = array()) {
        $this->stubsDir = $stubsDir;

        if (empty($stubsDir)) {
            $files = array();
        } else {
            $files = glob($stubsDir . '*');
        }

        $all = array_merge($files, $stubsFiles);
        foreach($all as $file) {
            if ($file[0] !== '/') {
                $file = $stubsDir . '/' . $file;
            }

            if (is_dir($file)) {
                continue;
            }
            if (!file_exists($file)) {
                continue;
            }

            if (substr($file, -5) === '.json' ) {
                $this->stubs[] = new StubJson($file);
            } elseif (substr($file, -5) === '.pdff' ) {
                $this->stubs[basename($file, '.pdff')] = new PdffReader($file);
            }
            // @todo : Format manuel?
        }
    }

    public function list(): array {
        return array_keys($this->stubs);
    }

    public function get(string $name): StubsInterface {
        if (!isset($this->stubs[$name])) {
            throw new \Exception('No such stub as ' . $name);
        }

        return $this->stubs[$name];
    }

    public function __call(string $name, array $args): array {
        assert(method_exists(StubsInterface::class, $name), "No such method as $name in stubs");

        if (empty($this->stubs)) {
            return array();
        }
        // Check on $name values
        $return = array();

        foreach($this->stubs as $stub) {
            assert(method_exists($stub, $name), "No such method as $name for Definition file");
            $return[] = $stub->$name();
        }

        return array_merge(...$return);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Stubs;

use Stdclass;

class PdffReader extends Stubs implements StubsInterface {
    private string $pdffFile    = '';
    private object $data        ;

    public function __construct(string $pdffFile) {
        $this->pdffFile = $pdffFile;

        $this->data = json_decode(file_get_contents($pdffFile)) ?? new Stdclass();
        assert(json_last_error() == 0, 'Could not decode ' . $pdffFile . '. ' . json_last_error_msg());
    }

    public function getFile(): array {
        return array( basename($this->pdffFile) );
    }

    public function getFunctionList(): array {
        $return = array(array());

        foreach($this->data->versions as $namespaces => $namespaceDefinitions) {
            foreach($namespaceDefinitions as $namespace => $definitions) {
                $functions = array_keys((array) ($definitions->functions ?? array()));
                $functions = array_map(function (string $f) use ($namespace): string { return $namespace . $f;}, $functions);
                $return[] = $functions;
            }
        }

        return array_merge(...$return);
    }

    public function getFunctionNamesList(): array {
        $return = array(array());

        foreach($this->data->versions as $namespaces => $namespaceDefinitions) {
            foreach($namespaceDefinitions as $namespace => $definitions) {
                $functions = array_column((array) ($definitions->functions ?? array()), 'name');
                $return[] = $functions;
            }
        }

        return array_merge(...$return);
    }


    public function getConstantList(): array {
        $return = array(array());

        foreach($this->data->versions as $namespaces => $namespaceDefinitions) {
            foreach($namespaceDefinitions as $namespace => $definitions) {
                $constants = array_keys((array) ($definitions->constants ?? array()));
                $constants = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $constants);
                $return[] = $constants;
            }
        }

        return array_merge(...$return);
    }

    public function getFunctionsArgsInterval(): array {
        $return = array(array());

        foreach($this->data->versions as $namespace => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $functions = (array) ($namespaceDetails->functions ?? array());
                $f = array();
                foreach($functions as $name => $F) {
                    $f[] = array('name' => $namespace . $name,
                                 'args_min' => $F->totalParameters ?? 0,
                                 'args_max' => ($F->totalParameters ?? 0) + ($F->optionalParameters ?? 0),
                                 );
                }
                $return[] = $f;
            }
        }

        return array_merge(...$return);
    }

    public function getConstructorsArgsInterval(): array {
        $return = array(array());

        foreach($this->data->versions as $namespace => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $classes = (array) ($namespaceDetails->classes ?? array());
                $f = array();
                foreach($classes as $name => $C) {
                    if (!isset($C->methods->__construct)) {
                        continue;
                    }

                    $f[] = array('name' => $namespace . $name . '::__construct',
                                 'args_min' => $C->methods->__construct->totalParameters ?? 0,
                                 'args_max' => ($C->methods->__construct->totalParameters ?? 0) + ($C->methods->__construct->optionalParameters ?? 0),
                                 );
                }
                $return[] = $f;
            }
        }

        return array_merge(...$return);
    }

    public function getMethodsArgsInterval(): array {
        $return = array(array());

        foreach($this->data->versions as $namespace => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $classes = (array) ($namespaceDetails->classes ?? array());
                foreach($classes as $name => $C) {
                    if (!isset($C->methods)) {
                        continue;
                    }
                    foreach($C->methods as $mname => $M) {
                        $f = array();
                        $f[] = array('name'     => $namespace . $name . '::' . $mname,
                                     'args_min' => $M->totalParameters ?? 0,
                                     'args_max' => ($M->totalParameters ?? 0) + ($M->optionalParameters ?? 0),
                                 );
                        $return[] = $f;
                    }
                }

                $traits = (array) ($namespaceDetails->traits ?? array());
                foreach($traits as $name => $T) {
                    if (!isset($T->methods)) {
                        continue;
                    }
                    foreach($T->methods as $mname => $M) {
                        $f = array();
                        $f[] = array('name'     => $namespace . $name . '::' . $mname,
                                     'args_min' => $M->totalParameters ?? 0,
                                     'args_max' => ($M->totalParameters ?? 0) + ($M->optionalParameters ?? 0),
                                 );
                        $return[] = $f;
                    }
                }
            }
        }

        return array_merge(...$return);
    }

    public function getInterfaceList(): array {
        $return = array(array());

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $interfaces = array_keys((array) ($namespaceDetails->interfaces ?? array()));
                $interfaces = array_map(function (string $i) use ($namespace): string { return $namespace . $i;}, $interfaces);
                $return[] = $interfaces;
            }
        }

        return array_merge(...$return);
    }

    public function getEnumList(): array {
        $return = array(array());

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $enums = array_keys((array) ($namespaceDetails->enums ?? array()));
                $enums = array_map(function (string $e) use ($namespace): string { return $namespace . $e;}, $enums);
                $return[] = $enums;
            }
        }

        return array_merge(...$return);
    }

    public function getTraitList(): array {
        $return = array(array());

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $traits = array_keys((array) ($namespaceDetails->traits ?? array()));
                $traits = array_map(function (string $t) use ($namespace): string { return $namespace . $t;}, $traits);
                $return[] = $traits;
            }
        }

        return array_merge(...$return);
    }

    public function getNamespaceList(): array {
        return array_keys((array) $this->data->versions);
    }

    public function getClassList(): array {
        $return = array(array());

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $classes = array_keys((array) ($namespaceDetails->classes ?? array()));
                $classes = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $classes);
                $return[] = $classes;
            }
        }

        return array_merge(...$return);
    }

    public function getClassConstantList(): array {
        $return = array(array());

        // @todo : how to handle when the constant is defined in a class/interface above
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $classConstants = array_keys((array) ($class->constants ?? array()));
                    $classConstants = array_map(function (string $constant) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $constant;}, $classConstants);
                    $return[] = $classConstants;
                }

                foreach($namespaceDetails->interfaces as $interface) {
                    $classConstants = array_keys((array) ($interface->constants ?? array()));
                    $classConstants = array_map(function (string $constant) use ($namespace, $interface): string { return mb_strtolower($namespace . $interface->name) . '::' . $constant;}, $classConstants);
                    $return[] = $classConstants;
                }

                foreach($namespaceDetails->enums as $enum) {
                    $classConstants = array_keys((array) ($enum->constants ?? array()));
                    $classConstants = array_map(function (string $constant) use ($namespace, $enum): string { return mb_strtolower($namespace . $enum->name) . '::' . $constant;}, $classConstants);
                    $return[] = $classConstants;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getEnumCasesList(): array {
        $return = array(array());

        // @todo : how to handle when the constant is defined in a class/interface above
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->enums as $enum) {
                    $cases = array_keys((array) ($enum->cases ?? array()));
                    $cases = array_map(function (string $case) use ($namespace, $enum): string { return mb_strtolower($namespace . $enum->name) . '::' . $case;}, $cases);
                    $return[] = $cases;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getClassPropertyList(): array {
        $return = array(array());

        $filter = function (Stdclass $property): bool { return $property->static === false;};
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $properties = array_keys(array_filter((array) $class->properties ?? array(), $filter));
                    $properties = array_map(function (string $property) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $property;}, $properties);
                    $return[] = $properties;
                }

                foreach($namespaceDetails->traits as $trait) {
                    $properties = array_keys( array_filter((array) $trait->properties ?? array(), $filter));
                    $properties = array_map(function (string $property) use ($namespace, $trait): string { return mb_strtolower($namespace . $trait->name) . '::' . $property;}, $properties);
                    $return[] = $properties;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getClassStaticPropertyList(): array {
        $return = array(array());

        $filter = function (Stdclass $property): bool { return $property->static === true;};
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $properties = array_keys(array_filter((array) $class->properties ?? array(), $filter));
                    $properties = array_map(function (string $property) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $property;}, $properties);
                    $return[] = $properties;
                }

                foreach($namespaceDetails->traits as $trait) {
                    $properties = array_keys(array_filter((array) $trait->properties ?? array(), $filter));
                    $properties = array_map(function (string $property) use ($namespace, $trait): string { return mb_strtolower($namespace . $trait->name) . '::' . $property;}, $properties);
                    $return[] = $properties;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getClassMethodList(): array {
        $return = array(array());

        $filter = function (Stdclass $property): bool { return $property->static === false;};
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $methods = array_keys((array_filter((array) ($class->methods ?? array()), $filter)));
                    $methods = array_map(function (string $method) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $method;}, $methods);
                    $return[] = $methods;
                }

                foreach($namespaceDetails->traits as $trait) {
                    $methods = array_keys(array_filter((array) $trait->methods ?? array(), $filter));
                    $methods = array_map(function (string $method) use ($namespace, $trait): string { return mb_strtolower($namespace . $trait->name) . '::' . $method;}, $methods);
                    $return[] = $methods;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getClassMethodNamesList(): array {
        $return = array(array());

        $filter = function (Stdclass $property): bool { return $property->static === false;};
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $methods = array_column((array_filter((array) ($class->methods ?? array()), $filter)), 'name');
                    $methods = array_map(function (string $method) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $method;}, $methods);
                    $return[] = $methods;
                }

                foreach($namespaceDetails->traits as $trait) {
                    $methods = array_column(array_filter((array) $trait->methods ?? array(), $filter), 'name');
                    $methods = array_map(function (string $method) use ($namespace, $trait): string { return mb_strtolower($namespace . $trait->name) . '::' . $method;}, $methods);
                    $return[] = $methods;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getClassStaticMethodList(): array {
        $return = array(array());

        $filter = function (Stdclass $property): bool { return $property->static === true;};
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $methods = array_keys(array_filter((array) ($class->methods ?? array()), $filter));
                    $methods = array_map(function (string $method) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $method;}, $methods);
                    $return[] = $methods;
                }

                foreach($namespaceDetails->traits as $trait) {
                    $methods = array_keys(array_filter((array) $trait->methods ?? array(), $filter));
                    $methods = array_map(function (string $method) use ($namespace, $trait): string { return mb_strtolower($namespace . $trait->name) . '::' . $method;}, $methods);
                    $return[] = $methods;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getPropertyList(): array {
        $return = array(array());

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class => $body) {

                    $classProperties = array_keys((array) ($body->properties ?? array()));
                    if (empty($classProperties)) {
                        continue;
                    }
                    $list = $body->properties;
                    $classProperties = array_filter($classProperties, function (string $property) use ($list): bool { return $list->{$property}->static === false; });
                    $classProperties = array_map(function (string $property) use ($namespace, $class): string { return $namespace . $class . '::' . $property;}, $classProperties);
                    $return[] = $classProperties;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getMethodList(): array {
        $return = array(array());

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class => $body) {

                    $classMethods = array_keys((array) ($body->methods ?? array()));
                    if (empty($classMethods)) {
                        continue;
                    }
                    $list = $body->methods;
                    $classMethods = array_filter($classMethods, function (string $method) use ($list): bool { return $list->{$method}->static === false; });
                    $classMethods = array_map(function (string $method) use ($namespace, $class): string { return $namespace . $class . '::' . $method;}, $classMethods);
                    $return[] = $classMethods;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getInterfaceMethodsNameAndCount(): array {
        $return = array();

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->interfaces as $interface => $body) {
                    foreach($body->methods as $name => $details) {
                        $return[mb_strtolower($namespace . $interface)][] = (object) array('name'  => $name,
                                                                                           'count' => count($details->parameters),
                            );
                    }
                }
            }
        }

        return $return;
    }

    public function getFinalClasses(): array {
        $return = array();

        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $classes = array_filter((array) $namespaceDetails->classes ?? array(), function (object $class) { return $class->final ?? false;});
                $classes = array_keys($classes);
                $classes = array_map(function (string $c) use ($namespace): string { return $namespace . $c;}, $classes);
                $return[] = $classes;
            }
        }

        return array_merge(...$return);
    }

    public function getFinalClassConstants(): array {
        $return = array(array());

        // @todo : how to handle when the constant is defined in a class/interface above
        foreach($this->data->versions as $version => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                foreach($namespaceDetails->classes as $class) {
                    $classConstants = (array) ($class->constants ?? array());
                    $classConstants = array_filter($classConstants, function (object $constant) { return $constant->final ?? false;});
                    $classConstants = array_keys($classConstants);
                    $classConstants = array_map(function (string $constant) use ($namespace, $class): string { return mb_strtolower($namespace . $class->name) . '::' . $constant;}, $classConstants);
                    $return[] = $classConstants;
                }

                foreach($namespaceDetails->interfaces as $interface) {
                    $classConstants = (array) ($interface->constants ?? array());
                    $classConstants = array_filter($classConstants, function (object $constant) { return $constant->final ?? false;});
                    $classConstants = array_keys($classConstants);
                    $classConstants = array_map(function (string $constant) use ($namespace, $interface): string { return mb_strtolower($namespace . $interface->name) . '::' . $constant;}, $classConstants);
                    $return[] = $classConstants;
                }

                foreach($namespaceDetails->enums as $enum) {
                    $classConstants = (array) ($enum->constants ?? array());
                    $classConstants = array_filter($classConstants, function (object $constant) { return $constant->final ?? false;});
                    $classConstants = array_keys($classConstants);
                    $classConstants = array_map(function (string $constant) use ($namespace, $enum): string { return mb_strtolower($namespace . $enum->name) . '::' . $constant;}, $classConstants);
                    $return[] = $classConstants;
                }
            }
        }

        return array_merge(...$return);
    }

    public function getClassImplementingList(): array {
        $return = array();

        foreach($this->data->versions as $namespace => $namespaceList) {
            foreach($namespaceList as $namespace => $namespaceDetails) {
                $classes = (array) ($namespaceDetails->classes ?? array());
                $f = array();
                foreach($classes as $name => $C) {
                    if (!empty($C->implements)) {
                        $return[mb_strtolower($namespace . $name)] = array_column($C->implements, 'target');
                    }
                }
            }
        }

        return $return;
    }

    public function getFunctionsReferenceArgs(): array {
        $return = array();
        /*
            array of array (function =>, position of the referenced argument)
        */

        foreach($this->data->versions as $namespaces => $namespaceDefinitions) {
            foreach($namespaceDefinitions as $namespace => $definitions) {
                foreach($definitions->functions ?? array() as $function => $details) {
                    foreach($details->parameters as $position => $parameter) {
                        if ($parameter->reference === true) {
                            $return[] = array('function' => $namespace . $details->name,
                                              'position' => $position
                                             );
                        }
                    }
                }
            }
        }

        return $return;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Stubs;


class StubIni extends Stubs implements StubsInterface {
    private $stubFile    = '';
    private $stub        = array();

    public function __construct(string $stubFile) {
        $this->stubFile = $stubFile;

        assert(file_exists($stubFile), "No data for $stubFile");
        $this->stub = parse_ini_file($stubFile);
    }

    public function getFile(): array {
        return array( basename($this->stubFile) );
    }

    public function getFunctionList(): array {
        return $this->stub->functions ?? array();
    }

    public function getConstantList(): array {
        return $this->stub->constants ?? array();
    }

    public function getFunctionsArgsInterval(): array {
        return array();
    }

    public function getInterfaceList(): array {
        return $this->stub->interfaces ?? array();
    }

    public function getTraitList(): array {
        return $this->stub->traits ?? array();
    }

    public function getClassList(): array {
        return $this->stub->classes ?? array();
    }

    public function getClassConstantList(): array {
        return $this->stub->staticConstants ?? array();
    }

    public function getClassPropertyList(): array {
        return $this->stub->staticProperties ?? array();
    }

    public function getClassMethodList(): array {
        return $this->stub->methods ?? array();
    }


    public function getPropertyList(): array {
        return $this->stub->properties ?? array();
    }

    public function getMethodList(): array {
        return $this->stub->methods ?? array();
    }

    public function getFinalClasses(): array {
        return array();
    }

    public function getFinalClassConstants(): array {
        return array();
    }

    public function getFunctionNamesList(): array {
        return array();
    }

    public function getClassMethodNamesList(): array {
        return array();
    }

    public function getNamespaceList(): array {
        return array();
    }

    public function getConstructorsArgsInterval(): array {
        return array();
    }

    public function getMethodsArgsInterval(): array {
        return array();
    }

    public function getEnumList(): array {
        return array();
    }

    public function getClassStaticPropertyList(): array {
        return array();
    }

    public function getClassStaticMethodList(): array {
        return array();
    }

    public function getEnumCasesList(): array {
        return array();
    }

    public function getInterfaceMethodsNameAndCount(): array {
        return array();
    }

    public function getClassImplementingList(): array {
        return array();
    }

    public function getFunctionsReferenceArgs(): array {
        return array();
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Log;

class Timing extends Log {
    private $start = null;
    private $begin = null;
    private $end   = null;

    public function __construct(string $name = '') {
        assert(!empty($name), 'Cannot use an empty-named timing log');
        parent::__construct($name);

        $this->begin = microtime(\TIME_AS_NUMBER);
        $this->start = microtime(\TIME_AS_NUMBER);
    }


    public function log(string $message): void {
        $this->end = microtime(\TIME_AS_NUMBER);

        parent::log($message . "\t" . ($this->end - $this->begin) . "\t" . ($this->end - $this->start));
        $this->begin = $this->end;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Log;

class Log {
    private $file = null;
    private $name = null;

    public function __construct(string $name = '') {
        $this->name = $name;

        // If name is really long, keep 240 chars, and create a crc32 at the end
        if (strlen($this->name) > 250) {
            $this->name = substr($this->name, 0, 240) . '-' . crc32($this->name);
        }

        $config = exakat('config');
        $dir = $config->project_dir;

        if (!file_exists("$dir/log/")) {
            return ;
        }

        if (!is_dir("$dir/log/")) {
            return ;
        }

        if (file_exists("$dir/log/{$this->name}.log")) {
            $this->file = fopen("$dir/log/{$this->name}.log", 'a');
        } else {
            $this->file = fopen("$dir/log/{$this->name}.log", 'w+');
        }

        if (!$this->file) {
            display("Couldn\'t create log file in $dir/log/");
            $this->file = null;
        }
    }


    public function log(string $message): void {
        if ($this->file === null) {
            return;
        }

        fwrite($this->file, $message . PHP_EOL);
    }
}

?>
   Bud1                                                                     spblob   bp                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  D S Lbwspblob   bplist00		]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar			_{{0, 329}, {840, 546}}	%1=I`myz{|}~                                D S LlsvCblob  &bplist00	
TUVWY_viewOptionsVersion_showIconPreview_calculateAllSizesWcolumns_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDatesXiconSize 	"&+05:?CGLPWvisibleUwidthYascendingZidentifier	Tname#Xubiquity!	\dateModified%[dateCreated(*	aTsize-/	s	Tkind24d	Ulabel79K	Wversion<>,	XcommentsB^dateLastOpenedFYdateAddedIKZshareOwnerIO_shareLastEditorIS_invitationStatus#@     #@(      #        Tname	#@0         2 D X ` r {                 	,567CLMOPU^_abgpqstz
(1:CHI             Z              R    D S Llsvpblob  bplist00	
FGHIK_viewOptionsVersion_showIconPreview_calculateAllSizesWcolumns_scrollPositionYXtextSize_scrollPositionXZsortColumn_useRelativeDatesXiconSize 	!&+059>BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpenedUindexUwidthYascendingWvisible,	"#d	'(K	,-12a	6-	:;s		?	C-#@     #@(      #        Tname	#@0         2 D X ` r {              
"(.8@BEFGPRTUV_acdenprst}             L                  D S LvSrnlong                                                                                                                                                                                                                                                                                                       E                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         DSDB                                 `                                                  @                                                @                                                @       !&+059>BXcommentsUlabelWversion[dateCreatedTsize\dateModifiedTkindTname^dateLastOpenedUindexUwidthYascendingWvisible,	"#d	'(K	,-12a	6-	:;s		?	C-#@     #@(      #        Tname	#@0         2 D X ` r {              
"(.8@BEFGPRTUV_acdenprst}             L                  D S LvSrnlong                                                                                                                                                                                                                                                                                           <?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

declare(strict_types = 1);

namespace Exakat\Query;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\DSLFactory;
use Exakat\Query\DSL\Command;
use Exakat\Project;

class Query2 extends Query {

    private const SACK = '.withSack(["m":[], "processed":0, "total":0])';

    private $id         = null;
    private $project    = null;
    private $analyzer   = null;
    private $php        = null;

    private $commands         = array();
    private $arguments        = array();
    private $query            = null;
    private $queryFactory     = null;
    private $sides            = array();
    private $stopped          = self::QUERY_RUNNING;

    public function __construct(int $id, Project $project, string $analyzer, array $dependsOn = array()) {
        $this->id        = $id;
        $this->project   = $project;
        $this->analyzer  = $analyzer;

        $this->queryFactory = new DSLFactory($analyzer, $dependsOn);
    }

    public function side(): self {
        if ($this->stopped === self::QUERY_STOPPED) {
            return $this;
        }

        $this->sides[] = $this->commands;
        $this->commands = array();

        return $this;
    }


    public function prepareSide(): Command {
        if ($this->stopped === self::QUERY_STOPPED) {
            return new Command(self::NO_QUERY);
        }

        $commands = array_column($this->commands, 'gremlin');

        assert(!empty($this->sides), 'No side was started! Missing $this->side() ? ');
        assert(!empty($commands), 'No command in side query');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            $this->commands = array_pop($this->sides);
            return new Command(self::STOP_QUERY);
        }

        $query = '__.' . implode(".\n", $commands);
        $args = array_column($this->commands, 'arguments');
        $args = array_merge(...$args);

        $query = str_replace(array_keys($args), '***', $query);

        $sack = $this->prepareSack($this->commands);

        $return = new Command($query, array_values($args));
        $return->setSack($sack);

        $this->commands = array_pop($this->sides);

        return $return;
    }

    public function __call(string $name, array $args): self {
        if ($this->stopped === self::QUERY_STOPPED) {
            return $this;
        }

        assert(!(empty($this->commands) && empty($this->sides)) || in_array(strtolower($name), array('atomis', 'analyzeris', 'atomfunctionis')), "First step in Query must be atomIs, atomFunctionIs or analyzerIs ($name used)");

        $command = $this->queryFactory->factory($name);
        if (in_array($name, array('not', 'filter', 'optional'))) {
            $chain = $this->prepareSide();
            $last = $command->run($chain);
        } else {
            $last = $command->run(...$args);
        }
        $this->commands[] = $last;

        if ($last->gremlin === self::STOP_QUERY && empty($this->sides)) {
            $this->query = "// Query with STOP_QUERY\n";
            $this->commands = array();

            $this->stopped = self::QUERY_STOPPED;

            return $this;
        }

        if (count($this->commands) === 1 && empty($this->sides)) {
            switch(strtolower($name)) {
                case 'atomis' :
                case 'atomfunctionis' :
                    $this->_as('first');
                    $this->raw('sack{m,v -> ++m["processed"]; m;}');
                    break;

                case 'analyzeris' :
                    $this->atomIs('Analysis', Analyzer::WITHOUT_CONSTANTS);
                    $this->commands = array($this->commands[1]);

                    $this->propertyIs('analyzer', $args[0], Analyzer::CASE_SENSITIVE);
                    $this->outIs('ANALYZED');
                    $this->_as('first');
                    $this->raw('sack{m,v -> ++m["processed"]; m;}');

                    $this->raw('groupCount("processed").by(count())');

                    break;

                default :
                    if ($this->commands[0]->gremlin === self::STOP_QUERY) {
                        $this->_as('first');
                        // Keep going
                    } else {
                        assert(false, 'No gremlin optimization : gremlin query "' . $name . '" in analyzer should have use g.V. ! ' . $this->commands[0]->gremlin);
                    }
            }
        }

        return $this;
    }

    public function prepareQuery(): bool {
        if ($this->stopped === self::QUERY_STOPPED) {
            return true;
        }

        assert($this->query === null, 'query is already ready');
        assert(empty($this->sides), 'sides are not empty : left ' . count($this->sides) . ' element');

        // @doc This is when the object is a placeholder for others.
        if (empty($this->commands)) {
            return true;
        }

        $sack = self::SACK;
        $this->query = "g{$sack}.V().";

        $commands  = array_column($this->commands, 'gremlin');
        $arguments = array_column($this->commands, 'arguments');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            // any 'stop_query' is blocking
            $this->query = '';
            return false;
        }

        foreach($commands as $id => $command) {
            if ($command === self::NO_QUERY) {
                unset($commands[$id], $arguments[$id]);
            }
        }

        $this->query .= implode(".\n", $commands);

        if (empty($arguments)) {
            $this->arguments = array();
        } else {
            $this->arguments = array_merge(...$arguments);
        }

        return true;
    }

    public function prepareRawQuery(): void {
        if ($this->stopped === self::QUERY_STOPPED) {
            return;
        }

        $commands = array_column($this->commands, 'gremlin');
        $arguments = array_column($this->commands, 'arguments');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            // any 'stop_query' is blocking
            $this->query = "// Query with STOP_QUERY\n";
            return ;
        }

        foreach($commands as $id => $command) {
            if ($command === self::NO_QUERY) {
                unset($commands[$id], $arguments[$id]);
            }
        }

        $commands = implode('.', $commands);
        $this->arguments = array_merge(...$arguments);

        $sack = self::SACK;

        $this->query = "g{$sack}.V()." . $commands;
    }

    public function printRawQuery(): void {
        $this->prepareRawQuery();

        print $this->query . PHP_EOL;
        print_r($this->arguments);
        die(__METHOD__);
    }

    public function getQuery(): string {
        assert($this->query !== null, 'Null Query found!');
        return $this->query;
    }

    public function getArguments(): array {
        return $this->arguments;
    }

    public function printQuery(): void {
        $this->prepareQuery();

        var_dump($this->query);
        print_r($this->arguments);
        die(__METHOD__);
    }

    private function prepareSack(array $commands) {
        foreach($commands as $command) {
            if ($command->getSack() === Command::SACK_NONE) {
                continue;
            }

            return $command->getSack();
        }

        return Command::SACK_NONE;
    }

    private function sackToGremlin(array $sack): string {
        if (empty($sack)) {
            return '';
        }

        $return = array();
        foreach($sack as $name => $init) {
            $return[] = "\"$name\":" . trim((string) $init, ' {}');
        }

        $return = '.withSack{[' . implode(', ', $return) . ']}';
        return $return;
    }

    public function canSkip(): bool {
        return $this->stopped !== self::QUERY_RUNNING;
    }
}
?><?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

declare(strict_types = 1);

namespace Exakat\Query;


class QueryDoc {
    private $stopped = null;
    private $commands = null;
    private $arguments = null;
    private $query = null;
    private $stats = array();

    private $steps = array();

    private $cursor    = 1;
    private $cursors   = array();
    private $nodes     = array(1=> 'root');
    private $links     = array();
    private $labels    = array('first' => 1, );

    public function __construct() {    }

    public function __call($name, $args) {
        if (in_array($name, array('not', 'filter', 'optional'))) {
            $chain = $this->prepareSide();
            $this->steps[] = $name . '[ ' . $chain . ' ]';
            print "$name\n";

            $next = array_pop($this->head);
            $this->nodes[$next] = "Node $name";
            $this->cursor = array_pop($this->cursors);

            $this->links[] = array($this->cursor, $next, $name);
            $this->cursor = $next;
        } elseif (in_array($name, array('back'))) {
            $this->steps[] = $name;
            print "$name\n";

            $this->cursor = $this->labels[$args[0]];
        } elseif (in_array($name, array('as'))) {
            $this->steps[] = $name;
            print "$name\n";

            $this->labels[$args[0]] = $this->cursor;
        } else {
            $this->steps[] = $name;
            print "$name\n";

            $this->nodes[] = "Node $name";
            $next = count($this->nodes);
            $this->links[] = array($this->cursor, $next, $name);
            $this->cursor = $next;
        }


        $this->stats[$name] = ($this->stats[$name] ?? 0) + 1;

    }

    public function side(): self {
        $this->sides[] = $this->steps;
        $this->steps = array('side');

        $this->cursors[] = $this->cursor;
        $this->nodes[] = 'Node SIDE';
        $next = count($this->nodes);
        $this->head[]    = $next;
        $this->cursor    = $next;

        $this->stats['side'] = ($this->stats['side'] ?? 0) + 1;

        print "  Side\n";

        return $this;
    }

    public function prepareSide() {
        print "  prepareSide\n";
        $chain = implode('-', $this->steps);
        $this->steps = array_pop($this->sides);

        return $chain;
    }

    public function prepareQuery(): bool {
        if ($this->stopped === self::QUERY_STOPPED) {
            return true;
        }

        assert($this->query === null, 'query is already ready');
        assert(empty($this->sides), 'sides are not empty : left ' . count($this->sides) . ' element');

        // @doc This is when the object is a placeholder for others.
        if (empty($this->commands)) {
            return true;
        }

        /*
        Sack is ignored ATM
        $sack = $this->prepareSack($this->commands);
        if (is_array($sack)) {
            $sack['processed'] = 0;
            $sack['total'] = 0;
        } else {
            $sack = array('processed' => 0,
                          'total' => 0,
                          );
        }
        $sack = $this->sackToGremlin($sack);
        */
        $sack = self::SACK;
        $this->query = "g{$sack}.V()";

        $commands  = array_column($this->commands, 'gremlin');
        $arguments = array_column($this->commands, 'arguments');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            // any 'stop_query' is blocking
            $this->query = '';
            return false;
        }

        foreach($commands as $id => $command) {
            if ($command === self::NO_QUERY) {
                unset($commands[$id], $arguments[$id]);
            }
        }

        $this->query .= '.' . implode(".\n", $commands);

        if (empty($arguments)) {
            $this->arguments = array();
        } else {
            $this->arguments = array_merge(...$arguments);
        }

        return true;
    }

    public function prepareRawQuery() {
        if ($this->stopped === self::QUERY_STOPPED) {
            return true;
        }

        $commands = array_column($this->commands, 'gremlin');
        $arguments = array_column($this->commands, 'arguments');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            // any 'stop_query' is blocking
            return $this->query = "// Query with STOP_QUERY\n";
        }

        foreach($commands as $id => $command) {
            if ($command === self::NO_QUERY) {
                unset($commands[$id], $arguments[$id]);
            }
        }

        $commands = implode('.', $commands);
        $this->arguments = array_merge(...$arguments);

        $sack = self::SACK;

        $this->query = <<<GREMLIN
g{$sack}.V().as('first').$commands

// Query (#{$this->id}) for {$this->analyzer}
// php {$this->php} analyze -p {$this->project} -P {$this->analyzer} -v\n

GREMLIN;

    }

    public function printRawQuery() {
        $this->prepareRawQuery();

        print $this->query . PHP_EOL;
        print_r($this->arguments);
        die(__METHOD__);
    }

    public function getQuery() {
        assert($this->query !== null, 'Null Query found!');
        return $this->query;
    }

    public function getArguments() {
        return $this->arguments;
    }

    public function printQuery() {
        $this->prepareQuery();

        var_dump($this->query);
        print_r($this->arguments);
        die(__METHOD__);
    }

    private function prepareSack(array $commands) {
        foreach($commands as $command) {
            if ($command->getSack() === Command::SACK_NONE) {
                continue;
            }

            return $command->getSack();
        }

        return Command::SACK_NONE;
    }

    private function sackToGremlin(array $sack): string {
        if (empty($sack)) {
            return '';
        }

        $return = array();
        foreach($sack as $name => $init) {
            $return[] = "\"$name\":" . trim((string) $init, ' {}');
        }

        $return = '.withSack{[' . implode(', ', $return) . ']}';
        return $return;
    }

    public function canSkip(): bool {
        return $this->stopped !== self::QUERY_RUNNING;
    }

    public function display(): void {
        print '(' . implode('-', $this->steps) . ')';

//        print_r($this->stats);
        $graph = array();

        foreach($this->nodes as $id => $node) {
            $graph[] = "$id [label=\"$node\"];\n";
        }

        foreach($this->links as list($a, $b, $label)) {
            $graph[] = "$a -> $b [label = \"$label\"];\n";
        }

        file_put_contents('/tmp/docs.dot', 'digraph{ ' . implode('', $graph) . '}');
    }
}
?><?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

declare(strict_types = 1);

namespace Exakat\Query;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\DSL\DSLFactory;
use Exakat\Query\DSL\Command;
use Exakat\Project;

class Query {
    public const STOP_QUERY = 'filter{ false; }';
    public const NO_QUERY   = 'filter{ true;  }';

    public const TO_GREMLIN = true;
    public const NO_GREMLIN = false;

    public const QUERY_RUNNING = true;
    public const QUERY_STOPPED = false;

    private const SACK = '.withSack(["m":[], "processed":0, "total":0])';

    private $id         = null;
    private $project    = null;
    private $analyzer   = null;
    private $php        = null;

    private $commands         = array();
    private $arguments        = array();
    private $query            = null;
    private $queryFactory     = null;
    private $sides            = array();
    private $stopped          = self::QUERY_RUNNING;

    public function __construct(int $id, Project $project, string $analyzer, string $php, array $dependsOn = array()) {
        $this->id        = $id;
        $this->project   = $project;
        $this->analyzer  = $analyzer;
        $this->php       = $php;

        $this->queryFactory = new DSLFactory($analyzer, $dependsOn);
    }

    public function __call(string $name, array $args): self {
        if ($this->stopped === self::QUERY_STOPPED) {
            return $this;
        }

        assert(!(empty($this->commands) && empty($this->sides)) || in_array(strtolower($name), array('atomis', 'analyzeris', 'atomfunctionis')), "First step in Query must be atomIs, atomFunctionIs or analyzerIs ($name used)");

        $command = $this->queryFactory->factory($name);
        if (in_array($name, array('not', 'filter', 'optional'), STRICT_COMPARISON)) {
            $chain = $this->prepareSide();
            $last = $command->run($chain);
        } else {
            $last = $command->run(...$args);
        }
        $this->commands[] = $last;

        if ($last->gremlin === self::STOP_QUERY && empty($this->sides)) {
            $this->query = "// Query with STOP_QUERY\n";
            $this->commands = array();

            $this->stopped = self::QUERY_STOPPED;

            return $this;
        }

        if (count($this->commands) === 1 && empty($this->sides)) {
            switch(strtolower($name)) {
                case 'atomis' :
                case 'atomfunctionis' :
                    $this->_as('first');
                    $this->raw('sack{m,v -> ++m["processed"]; m;}');
                    break;

                case 'analyzeris' :
                    $this->atomIs('Analysis', Analyzer::WITHOUT_CONSTANTS);
                    $this->commands = array($this->commands[1]);

                    $this->propertyIs('analyzer', $args[0], Analyzer::CASE_SENSITIVE);
                    $this->outIs('ANALYZED');
                    $this->_as('first');
                    $this->raw('sack{m,v -> ++m["processed"]; m;}');

                    $this->raw('groupCount("processed").by(count())');

                    break;

                default :
                    if ($this->commands[0]->gremlin === self::STOP_QUERY) {
                        $this->_as('first');
                        // Keep going
                    } else {
                        assert(false, 'No gremlin optimization : gremlin query "' . $name . '" in analyzer should have use g.V. ! ' . $this->commands[0]->gremlin);
                    }
            }
        }

        return $this;
    }

    public function side(): self {
        if ($this->stopped === self::QUERY_STOPPED) {
            return $this;
        }

        $this->sides[] = $this->commands;
        $this->commands = array();

        return $this;
    }

    public function prepareSide(): Command {
        if ($this->stopped === self::QUERY_STOPPED) {
            return new Command(self::NO_QUERY);
        }

        $commands = array_column($this->commands, 'gremlin');

        assert(!empty($this->sides), 'No side was started! Missing $this->side() ? ');
        assert(!empty($commands), 'No command in side query');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            $this->commands = array_pop($this->sides);
            return new Command(self::STOP_QUERY);
        }

        $query = '__.' . implode(".\n", $commands);
        $args = array_column($this->commands, 'arguments');
        $args = array_merge(...$args);

        $query = str_replace(array_keys($args), '***', $query);

        $sack = $this->prepareSack($this->commands);

        $return = new Command($query, array_values($args));
        $return->setSack($sack);

        $this->commands = array_pop($this->sides);

        return $return;
    }

    public function prepareQuery(): bool {
        if ($this->stopped === self::QUERY_STOPPED) {
            return true;
        }

        assert($this->query === null, 'query is already ready');
        assert(empty($this->sides), 'sides are not empty : left ' . count($this->sides) . ' element');

        // @doc This is when the object is a placeholder for others.
        if (empty($this->commands)) {
            return true;
        }

        $sack = self::SACK;
        $this->query = "g{$sack}.V()";

        $commands  = array_column($this->commands, 'gremlin');
        $arguments = array_column($this->commands, 'arguments');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            // any 'stop_query' is blocking
            $this->query = '';
            return false;
        }

        foreach($commands as $id => $command) {
            if ($command === self::NO_QUERY) {
                unset($commands[$id], $arguments[$id]);
            }
        }

        $this->query .= '.' . implode(".\n", $commands);

        if (empty($arguments)) {
            $this->arguments = array();
        } else {
            $this->arguments = array_merge(...$arguments);
        }

        return true;
    }

    public function prepareRawQuery(): void {
        if ($this->stopped === self::QUERY_STOPPED) {
            return;
        }

        $commands = array_column($this->commands, 'gremlin');
        $arguments = array_column($this->commands, 'arguments');

        if (in_array(self::STOP_QUERY, $commands) !== false) {
            // any 'stop_query' is blocking
            $this->query = "// Query with STOP_QUERY\n";
            return ;
        }

        foreach($commands as $id => $command) {
            if ($command === self::NO_QUERY) {
                unset($commands[$id], $arguments[$id]);
            }
        }

        $commands = implode('.', $commands);
        $this->arguments = array_merge(...$arguments);

        $sack = self::SACK;

        $this->query = "g{$sack}.V()" . $commands;
    }

    public function printRawQuery(): void {
        $this->prepareRawQuery();

        print $this->query . PHP_EOL;
        print_r($this->arguments);
        die(__METHOD__);
    }

    public function getQuery(): string {
        assert($this->query !== null, 'Null Query found!');
        return $this->query;
    }

    public function getArguments(): array {
        return $this->arguments;
    }

    public function printQuery(): void {
        $this->prepareQuery();

        var_dump($this->query);
        print_r($this->arguments);
        die(__METHOD__);
    }

    private function prepareSack(array $commands) {
        foreach($commands as $command) {
            if ($command->getSack() === Command::SACK_NONE) {
                continue;
            }

            return $command->getSack();
        }

        return Command::SACK_NONE;
    }

    private function sackToGremlin(array $sack): string {
        if (empty($sack)) {
            return '';
        }

        $return = array();
        foreach($sack as $name => $init) {
            $return[] = "\"$name\":" . trim((string) $init, ' {}');
        }

        $return = '.withSack{[' . implode(', ', $return) . ']}';
        return $return;
    }

    public function canSkip(): bool {
        return $this->stopped !== self::QUERY_RUNNING;
    }
}
?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsLowercase extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($property) = func_get_args();
        } else {
            $property = 'fullcode';
        }

        return new Command('filter{it.get().value("' . $property . '") == it.get().value("' . $property . '").toLowerCase()}');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class GoToInstruction extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($atoms) = func_get_args();
        } else {
            $atoms = 'Namespaces';
        }

        $this->assertAtom($atoms);
        $diff = $this->normalizeAtoms($atoms);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $atomAndFile = $diff;
        $atomAndFile[] = 'File';
        $atomAndFile = array_unique($atomAndFile);

        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
repeat( __.in({$linksDown})).until(hasLabel(within(***)) )
          .hasLabel(within(***))
GREMLIN;
        return new Command($gremlin, array($atomAndFile, $diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotEmptyArray extends DSL {
    public function run(): Command {
        return new Command('not( where( __.hasLabel("Arrayliteral").has("count", 0)))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CompatibleWithType extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 2 :
                list($types, $withNull) = func_get_args();
                $withNull = in_array($withNull, array(NotCompatibleWithType::ALLOW_NULL, NotCompatibleWithType::DISALLOW_NULL), STRICT_COMPARISON) ? $withNull : NotCompatibleWithType::DISALLOW_NULL;
                break;

            case 1:
                list($types) = func_get_args();
                $withNull = NotCompatibleWithType::DISALLOW_NULL;
                break;

            default:
                assert(func_num_args() <= 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        }

        if ($withNull === NotCompatibleWithType::ALLOW_NULL) {
            $withNullGremlin = '.not(hasLabel("Null"))';
        } else {
            $withNullGremlin = '';
        }

        $query = <<<GREMLIN
where( 
__.sideEffect{ typehints = []; }
  .out("TYPEHINT", "RETURNTYPE")
  .has("fullnspath")
  $withNullGremlin
  .sideEffect{ typehints.add(it.get().value("fullnspath")) ; }
  .fold() 
)
.filter{
    results = false;
    for(typehint in typehints) {
        switch(typehint) {
            case "\\\\string":
                results = results || ($types in ["Magicconstant", "Heredoc", "String", "Concatenation", "Staticclass", "Shell"]);
                break;
                
            case "\\\\int":
                results = results || ($types in ["Integer", "Addition", "Multiplication", "Bitshift", "Logical", "Bitoperation", "Power", "Postplusplus", "Preplusplus", "Not", "Spaceship"]);
                break;
    
            case "\\\\numeric":
                results = results || ($types in ["Integer", "Addition", "Multiplication", "Bitshift", "Logical", "Bitoperation", "Power", "Float", "Postplusplus", "Preplusplus", "Spaceship"]);
                break;
    
            case "\\\\float":
                results = results || ($types in ["Float", "Addition", "Multiplication", "Bitshift", "Power", "Spaceship"]);
                break;
    
            case "\\\\bool":
                results = results || ($types in ["Boolean", "Logical", "Not", "Comparison"]);
                break;
    
            case "\\\\array":
                results = results || ($types in ["Arrayliteral", "Addition"]);
                break;
    
            case "\\\\mixed":
                results = true; // anything is mixed, so this is always false
                break;

            case "\\\\null":
                results = results || !($types in ["Null"]);
                break;
    
            case "\\\\false":
                results = results && !($types in ["Boolean"]);
                break;

            case "\\\\void":
            case "\\\\resource":
            default: 
                true;
        }
    }
    
    results;
}
GREMLIN;
        return new Command($query, array());
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class AtomFunctionIs extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $functioncallIs = $this->dslfactory->factory('functioncallIs');
        return $functioncallIs->run($fullnspath);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class FollowParAs extends DSL {
    public const FOLLOW_ALL        = 0;
    public const FOLLOW_NONE       = 1;
    public const FOLLOW_PARAS_ONLY = 2;

    public function run(): Command {

        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($out) = func_get_args();

        switch($out) {
            case self::FOLLOW_ALL:
                $out    = 'out(' . self::$linksDown . ').';
                $labels = '';
                $follow = '';
                break 1;

            case self::FOLLOW_NONE:
                $out    = 'identity().';
                $labels = '';
                $follow = '';
                break 1;

            case self::FOLLOW_PARAS_ONLY:
                $out    = 'identity().';
                $labels = '';
                $follow = '';
                break 1;

            default:
                $this->assertLink($out);
                $out = $this->normalizeLinks($out);

                if (empty($out)) {
                    return new Command(Query::STOP_QUERY);
                }

                $out = 'out(' . makeList($out) . ').';
                $labels = ', "Ternary", "Coalesce"';
                $follow = ', 
                __.hasLabel("Ternary").where(__.out("THEN").not(hasLabel("Void"))).out("THEN", "ELSE"), 
                __.hasLabel("Ternary").where(__.out("THEN").    hasLabel("Void" )).out("CONDITION", "ELSE"), 
                __.hasLabel("Coalesce").out("RIGHT", "LEFT")';
        }

        $TIME_LIMIT = self::$TIME_LIMIT;
        return new Command(<<<GREMLIN
 {$out}emit().repeat( 
    __.timeLimit($TIME_LIMIT)
      .coalesce(__.hasLabel("Parenthesis").out("CODE"), 
                __.hasLabel("Assignation").out("RIGHT")$follow
      )
).until(__.not(hasLabel("Parenthesis", "Assignation", $labels)))
.not(hasLabel("Parenthesis" $labels))
.not(hasLabel("Assignation").has("token", "T_EQUAL"))
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToFirstExpression extends DSL {
    public function run(): Command {
        $linksDown = self::$linksDown;

        return new Command(<<<GREMLIN
until( __.in($linksDown).not(hasLabel(within(***))) ).repeat( __.in($linksDown) )

GREMLIN
, array(Analyzer::EXPRESSION_ATOMS));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectExtends extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($variable) = func_get_args();
        } else {
            $variable = 'classes';
        }

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $MAX_LOOPING = self::$MAX_LOOPING;

        return new Command(<<<GREMLIN
where( 
    __.sideEffect{ $variable = []; }
      .repeat( __.out("IMPLEMENTS", "EXTENDS").in("DEFINITION") ).emit().times($MAX_LOOPING)
      .hasLabel("Class")
      .sideEffect{ $variable.add(it.get().value("fullnspath")) ; }
      .fold() 
) 

GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsNotLocalClass extends DSL {
    public function run(): Command {
        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
sideEffect{ inside = it.get().value("fullnspath"); }
.where(  __.repeat( __.in({$linksDown}) ).until( hasLabel("Class") ).filter{ it.get().value("fullnspath") == inside; }.count().is(eq(0)) )

GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;
use Exakat\Exceptions\WrongNumberOfArguments;

class SamePropertyAs extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($property, $name) = func_get_args();
                $caseSensitive = Analyzer::CASE_SENSITIVE;
                break;

            case 3:
                list($property, $name, $caseSensitive) = func_get_args();
                break;

            default:
                throw new WrongNumberOfArguments(__METHOD__, func_num_args(), 2);
        }

        if ($property !== SavePropertyAs::ATOM) {
            $this->assertProperty($property);
        }
        $this->assertVariable($name);

        if ($property === 'label') {
            return new Command('filter{ it.get().label() == ' . $name . '}');
        } elseif ($property === 'id') {
            return new Command('filter{ it.get().id() == ' . $name . '}');
        } elseif ($property === 'self') {
            assert(false, 'Dont use self with property');
        } elseif ($property === SavePropertyAs::ATOM) {
            return new Command('filter{ it.get() == ' . $name . '}');
        } elseif (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
            return new Command('filter{ if ( it.get().properties("' . $property . '").any()) { ' . $name . ' == it.get().value("' . $property . '")} else {' . $name . ' == false; }; }');
        } elseif ($property === 'intval') {
            return new Command('has("intval").filter{ it.get().value("intval") == ' . $name . '}');
        } elseif (in_array($property, array('reference'), \STRICT_COMPARISON) ) {
            return new Command('filter{ if (it.get().properties("' . $property . '").any()) { ' . $name . ' == it.get().value("' . $property . '");} else { ' . $name . ' == false; }}');
        } elseif ($property === 'code' || $property === 'lccode') {
            if ($caseSensitive === Analyzer::CASE_SENSITIVE) {
                return new Command('filter{ it.get().value("code") == ' . $name . '}');
            } else {
                return new Command('filter{ it.get().value("lccode") == ' . $name . '}');
            }
        } elseif (in_array($property, self::INTEGER_PROPERTY, \STRICT_COMPARISON)) {
            return new Command('filter{ it.get().value("' . $property . '") == ' . $name . '}');
        } else {
            $caseSensitive = $caseSensitive === Analyzer::CASE_SENSITIVE ? '' : '.toLowerCase()';

            return new Command('filter{ it.get().value("' . $property . '")' . $caseSensitive . ' == ' . $name . $caseSensitive . '}');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class OutIsWithLink extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 1, 'Too many arguments for ' . __METHOD__);

        if (func_num_args() == 0) {
            return new Command('out( )');
        }

        list($link) = func_get_args();

        if (empty($link)) {
            return new Command('out( )');
        }

        $this->assertLink($link);
        $diff = $this->normalizeLinks($link);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        } else {
            $SorA = $this->SorA($diff);
            return new Command(<<<GREMLIN
       union(__.identity(),
             __.out($SorA)
             )

GREMLIN
);
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInsideNoBlock extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $gremlin = 'emit( ).repeat( __.out(' . self::$linksDown . ').not(hasLabel("Sequence")) ).times(' . self::$MAX_LOOPING . ').hasLabel(within(***))';
        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class SelectMap extends DSL {
    public function run(): Command {
        list($values) = func_get_args();
        // @todo : add checks on existence ?

        $command = array();
        foreach($values as $key => $value) {
            assert(is_string($value), " SelectMap value vor $key is not a string! ");
            $command[] = "\"$key\": $value,";
        }
        $command = 'map { [' . implode(PHP_EOL, $command) . ']}';

        return new Command($command);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class FullnspathIsNot extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($code) = func_get_args();
            $caseSensitive = Analyzer::CASE_SENSITIVE;
        } else {
            list($code, $caseSensitive) = func_get_args();
        }

        $propertyIsNot = $this->dslfactory->factory('propertyIsNot');
        if (!in_array($code, $this->availableVariables, STRICT_COMPARISON)) {
            $code = makeArray($code);
        }

        return $propertyIsNot->run('fullnspath', $code, $caseSensitive);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasOut extends DSL {
    public function run(): Command {
        list($link) = func_get_args();

        $this->assertLink($link);
        $diff = $this->normalizeLinks($link);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        } else {
            return new Command('where( __.out(' . $this->SorA($diff) . ') )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class ProcessDereferencing extends DSL {
    public function run(): Command {
        list($tooManyDereferencing) = func_get_args();

        $command = new Command(<<<GREMLIN
where(
__.sideEffect{levels=0;}
        .repeat( __.sideEffect{levels += ["Array", "Arrayappend", "Member", "Methodcall", "Staticmethodcall"].contains( it.get().label() ) ? 1 : 0; } 
               .out("CLASS", "OBJECT", "VARIABLE", "APPEND", "CODE", "RIGHT"))
               .until(__.not(hasLabel("Array", "Arrayappend", "Member", "Staticproperty", "Methodcall", "Staticmethodcall", "Staticconstant", "Assignation", "Parenthesis")))
        .filter{ levels > $tooManyDereferencing}

)
GREMLIN
);

        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class FullcodeVariableIs extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($variable) = func_get_args();

        return new Command("filter{it.get().value(\"fullcode\") == $variable; }");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsLiteral extends DSL {
    public function run(): Command {
        // Closures are literal if not using a variable from the context
        return new Command(<<<'GREMLIN'
hasLabel("Integer", "Boolean", "Null", "Magicconstant", "Float", "String", "Heredoc", "Closure", "Arrayliteral").has("constant", true)

GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToFile extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run('File');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run('Trait');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class Ignore extends DSL {
    public function run(): Command {
        return new Command(Query::NO_QUERY);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NotImplementing extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $MAX_LOOPING = self::$MAX_LOOPING;
        return new Command(<<<GREMLIN
not(
    where( __.emit().repeat( __.out("IMPLEMENTS", "EXTENDS").in("DEFINITION")).times({$MAX_LOOPING})
                               .out("IMPLEMENTS").has("fullnspath", within(***)) ) 
)
GREMLIN
, array($fullnspath));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasNotAttribute extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($attributes) = func_get_args();

        $goToClass = $this->dslfactory->factory('goToClass');
        $return = $goToClass->run();

        $goToAllParents = $this->dslfactory->factory('goToAllParents');
        $return->add($goToAllParents->run(Analyzer::INCLUDE_SELF));

        $outIs = $this->dslfactory->factory('outIs');
        $return->add($outIs->run('ATTRIBUTE'));

        $is = $this->dslfactory->factory('is');
        $return->add($is->run('fullnspath', makeArray($attributes)));

        $not = $this->dslfactory->factory('not');

        return $not->run($return);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToAllImplements extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($self) = func_get_args();
        } else {
            $self = Analyzer::INCLUDE_SELF;
        }


        if ($self === Analyzer::EXCLUDE_SELF) {
            $command = new Command(<<<'GREMLIN'
out("IMPLEMENTS", "EXTENDS").in("DEFINITION")
GREMLIN
);
            return $command;
        } else {
            $command = new Command(<<<'GREMLIN'
union(
    __.identity(),
    __.out("IMPLEMENTS", "EXTENDS").in("DEFINITION")
)
GREMLIN
);
            return $command;
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class HasNoFunctionDefinition extends DSL {
    public function run(): Command {
        return new Command('not( where( __.in("DEFINITION").hasLabel("Function", "Method", "Magicmethod", "Closure", "Arrowfunction") ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectValues extends DSL {
    public function run(): Command {
        list($variable, $property) = func_get_args();

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $command = new Command(<<<GREMLIN
 sideEffect{ {$variable} = []; }
.where( 
    __.out("ARGUMENT")
      .optional(
        __.out("VALUE")
      )
      .sideEffect{ {$variable}.add(it.get().value("$property")) ; }
      .fold() 
    )
GREMLIN
);
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoCountedInstruction extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($atom, $count) = func_get_args();
                break;

            case 1:
                list($atom) = func_get_args();
                $count = '0';
                break;

            case 0:
            default:
                $atom = 'Function';
                $count = '0';
                break;
        }

        assert($this->assertAtom($atom));
        assert($count >= 0);
        $atom = makeArray($atom);

        // $count is an integer or a variable

        $stop = array('File', 'Closure', 'Function', 'Method', 'Class', 'Trait', 'Classanonymous');
        $stop = array_unique(array_diff($stop, $atom));
        $linksDown = self::$linksDown;

        return new Command(<<<GREMLIN
where( 
 __.sideEffect{ c = 0; }
   .emit( ).repeat(__.in($linksDown)) )
   .until(hasLabel(within(***)))
   .hasLabel(within(***))
   .sideEffect{ c = c + 1; }.fold()
).filter{ c < $count}
GREMLIN
, array($stop, $atom));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotArgument extends DSL {
    public function run(): Command {
        return new Command('not( where( __.in("DEFINITION").where( __.in("NAME"))) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class NoAnalyzerInside extends DSL {
    public function run(): Command {
        list($atoms, $analyzer) = func_get_args();

        $this->assertAnalyzer($analyzer);
        $this->assertAtom($atoms);
        $diff = $this->normalizeAtoms($atoms);

        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        }

        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

$gremlin = <<<GREMLIN
not( 
    where( __.emit( ).repeat( __.out({$linksDown}) ).times($MAX_LOOPING)
             .hasLabel(within(***))
             .where( __.in("ANALYZED").has("analyzer", within(***)))
          )     
)
GREMLIN;
        return new Command($gremlin,
                           array($diff, makeArray($analyzer)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsGlobalCode extends DSL {
    public function run(): Command {
        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
not( 
    where( 
        __.repeat(__.in({$linksDown}))
          .emit().until(hasLabel("File"))
          .hasLabel("Function", "Method", "Closure", "Magicmethod") 
          ) 
    )
GREMLIN;
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoAtomWithoutPropertyInside extends DSL {
    public function run(): Command {
        list($atoms, $property, $values) = func_get_args();

        assert($this->assertAtom($atoms));
        assert($this->assertProperty($property));
        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

$gremlin = <<<GREMLIN
not(
    where( __.emit( ).repeat( __.out($linksDown).not(hasLabel("Closure", "Classanonymous", "Closure", "Function", "Trait", "Interface")) )
                     .times($MAX_LOOPING)
                     .hasLabel(within(***))
                     .has("$property")
                     .has("$property", without(***)) 
        ) 
    )
GREMLIN;
        return new Command($gremlin,
                           array(makeArray($atoms), makeArray($values) ) );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class IsLessHash extends DSL {
    public function run(): Command {
        assert(func_num_args() === 3, 'Wrong number of argument for ' . __METHOD__ . '. 3 are expected, ' . func_num_args() . ' provided');
        list($property, $hash, $index) = func_get_args();

        if (empty($hash)) {
            return new Command(Query::STOP_QUERY);
        }

        assert($this->assertProperty($property));

        return new Command("has(\"$property\").filter{ it.get().value(\"$property\") < ***[$index]; }", array($hash));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class TokenIs extends DSL {
    public function run(): Command {
        list($token) = func_get_args();

        assert($this->assertTokens($token));
        return new Command('has("token", within(***))', array(makeArray($token)) );
    }
}
?>
   Bud1            %                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   E   %                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       DSDB                             `                                                     @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              <?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoAtomPropertyInside extends DSL {
    public function run(): Command {
        list($atom, $property, $values) = func_get_args();

        assert($this->assertAtom($atom));
        assert($this->assertProperty($property));
        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

        // Check with Structures/Unpreprocessed
        $gremlin = <<<GREMLIN
not(
    where( __.emit( ).repeat( __.out($linksDown).not(hasLabel("Closure", "Classanonymous")) )
                     .times($MAX_LOOPING).hasLabel(within(***))
                     .has("$property")
                     .has("$property", within(***))
          ) 
    )
GREMLIN;

        return new Command($gremlin, array(makeArray($atom), makeArray($values)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotEmptyBody extends DSL {
    public function run(): Command {
        $gremlin = <<<'GREMLIN'
not(
    where( 
        __.out("EXPRESSION")
          .not(hasLabel("Void", "Global", "Static"))
          .not(
            where( 
                __.hasLabel("Return")
                  .out("RETURN")
                  .hasLabel("Void", "Null")
                )
            )
        )
)
GREMLIN;

/*
          .not(
            where( 
                __.map( 
                    __.out("EXPRESSION").order().by("rank").tail(1)
                  )
                  .hasLabel("Return")
                  .out("RETURN")
                  .hasLabel("Void", "Null")
                )
            )

*/

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoInterfaceDefinition extends DSL {
    public function run(): Command {
        return new Command('not( where(__.in("DEFINITION").hasLabel("Interface") ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class AddAtom extends DSL {
    public function run(): Command {
        $this->assertArguments(2, func_num_args(), __METHOD__);
        list($atom, $properties) = func_get_args();

        $return = new Command('addV("' . $atom . '")');

        foreach($properties as $name => $value) {
            if (!$this->isVariable((string) $value)) {
                $value = $this->protectValue($value);
            }
            $sideEffect = new Command('sideEffect{ it.get().property("' . $name . '", ' . $value . ');}');

            $return->add($sideEffect);
        }

        return $return;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoNamedInstruction extends DSL {
    public function run(): Command {
        list($atom, $name) = func_get_args();

        assert($this->assertAtom($atom));
        if ($name === null) {
            return $this->hasNoInstruction($atom);
        }

        $gremlin = <<<'GREMLIN'
not( 
    where( 
        __.repeat( __.inE().not(hasLabel("DEFINITION", "ANALYZED")).outV()).until(hasLabel("File"))
          .hasLabel(within(***))
          .has("code", ***)
          ) 
    )
GREMLIN
;

        return new Command($gremlin, array(makeArray($atom), $name));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasNoOut extends DSL {
    public function run(): Command {
        list($links) = func_get_args();

        assert($this->assertLink($links));
        $diff = $this->normalizeLinks($links);

        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        } else {
            return new Command('not( where( __.out(' . $this->SorA($diff) . ') ) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class SaveNullableAs extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 1, __METHOD__ . ' should get 1 arguments max, ' . func_num_args() . ' provided.');

        list($variable) = func_get_args();

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        return new Command(<<<GREMLIN
choose(__.or(__.where( __.out("RETURNTYPE", "TYPEHINT").hasLabel("Null") ),
                      __.where( __.out("DEFAULT").hasLabel("Null").not(__.in("LEFT"))) 
                     ),
                      __.sideEffect{ {$variable} = true;},
                      __.sideEffect{ {$variable} = false;}
                   )
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Values extends DSL {
    public function run(): Command {
        list($property) = func_get_args();

        assert($this->assertProperty($property));

        return new Command("values(\"$property\")");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsNotPropertyDefined extends DSL {
    public function run(): Command {
        // check for DEFINTION link and the Virtualproperty atom
        return new Command('where( __.in("DEFINITION").hasLabel("Virtualproperty").not( __.out("OVERWRITE").hasLabel("Propertydefinition")))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasChildWithRank extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($links, $rank) = func_get_args();
        } else {
            list($links) = func_get_args();
            $rank = 0;
        }

        $this->assertLink($links);
        $diff = $this->normalizeLinks($links);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        return new Command('where( __.out(' . $this->SorA($diff) . ').has("rank", ***).not(hasLabel("Void")) )', array(abs((int) $rank)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToExtends extends DSL {
    public function run(): Command {
        return new Command('out("EXTENDS").in("DEFINITION")');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class OutWithRank extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($link, $rank) = func_get_args();

        if ($rank === 'first') {
            return new Command('out("' . $link . '").has("rank", eq(0))');
        } elseif ($rank === 'last') {
            return new Command('map( __.out("' . $link . '").order().by("rank").tail(1) )');
        } elseif ($rank === '2last') {
            return new Command('map( __.out("' . $link . '").order().by("rank").tail(2) )');
        } elseif (is_string($rank) && preg_match('/\D/', $rank)) {
            $this->assertVariable($rank, self::VARIABLE_READ);
            return new Command('out("' . $link . '").filter{ it.get().value("rank") == ' . $rank . '; }');
        } else { // abs((int) $rank) always works, and default to 0
            return new Command('out("' . $link . '").has("rank", eq(' . abs((int) $rank) . '))');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasConstantDefinition extends DSL {
    public function run(): Command {
        return new Command('where( __.in("DEFINITION").hasLabel("Constant", "Defineconstant"))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class HasNextSibling extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = 'EXPRESSION';
        }

        $hasIn = $this->dslfactory->factory('hasIn');
        $return = $hasIn->run($link);

        return $return->add(new Command('where( __.sideEffect{sibling = it.get().value("rank");}.in("' . $link . '").out("' . $link . '").filter{sibling + 1 == it.get().value("rank")})'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class CountArrayDimension extends DSL {
    public function run(): Command {
        list($variable) = func_get_args();

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $command = new Command('sideEffect{ ' . $variable . ' = 0; }
.repeat( __.in("VARIABLE", "APPEND").hasLabel("Array", "Arrayappend").sideEffect{ l = l + 1;}).emit()');

        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class HasTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run('Trait');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToClass extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(Analyzer::CLASSES_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoTryCatch extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run('Try');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NotCompatibleWithType extends DSL {
    public const DISALLOW_NULL = false;
    public const ALLOW_NULL = true;

    public function run(): Command {
        switch(func_num_args()) {
            case 2 :
                list($types, $withNull) = func_get_args();
                $withNull = in_array($withNull, array(self::ALLOW_NULL, self::DISALLOW_NULL), STRICT_COMPARISON) ? $withNull : self::DISALLOW_NULL;
                break;

            case 1:
                list($types) = func_get_args();
                $withNull = self::DISALLOW_NULL;
                break;

            default:
                assert(func_num_args() <= 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        }

        if ($withNull === self::ALLOW_NULL) {
            $withNullGremlin = '.not(hasLabel("Null"))';
        } else {
            $withNullGremlin = '';
        }

        $query = <<<GREMLIN
where( 
__.sideEffect{ typehints = []; }
  .out("TYPEHINT", "RETURNTYPE")
  .has("fullnspath")
  $withNullGremlin
  .sideEffect{ typehints.add(it.get().value("fullnspath")) ; }
  .fold() 
)
.filter{
    results = true;
    typehinttype = it.get().value("typehint");

    for(typehint in typehints) {
        switch(typehint) {
            case "\\\\string":
                result = !($types in ["Magicconstant", "Heredoc", "String", "Concatenation", "Staticclass", "Shell"]);
                break;
                
            case "\\\\int":
                result = !($types in ["Integer", "Addition", "Multiplication", "Bitshift", "Logical", "Bitoperation", "Power", "Postplusplus", "Preplusplus", "Not"]);
                break;
    
            case "\\\\numeric":
                result = !($types in ["Integer", "Addition", "Multiplication", "Bitshift", "Logical", "Bitoperation", "Power", "Float", "Postplusplus", "Preplusplus"]);
                break;
    
            case "\\\\float":
                result = !($types in ["Float", "Addition", "Multiplication", "Bitshift", "Power"]);
                break;
    
            case "\\\\bool":
                result = !($types in ["Boolean", "Logical", "Not", "Comparison"]);
                break;
    
            case "\\\\array":
                result = !($types in ["Arrayliteral", "Addition"]);
                break;
    
            case "\\\\mixed":
                result = false; // anything is mixed, so this is always false
                break;

            case "\\\\null":
                result = !($types in ["Null"]);
                break;

            case "\\\\false":
                result = !($types in ["Boolean"]) || (fqn2 == "\\\\true"); 
                break;
    
            case "\\\\void":
            case "\\\\resource":
                false;

            // This is actually the case for all others types, so classes and interfaces
            // we may also end up with any new PHP native type there. 
            default:   
                result = !($types in ["New"]) || (fqn != typehint)
        }
        
        if (typehinttype == "one") {
            // straight from the comparison
            results = result;
        } else if (typehinttype == "or") {
            // One of them is sufficient 
            results = results && result;
        } else if (typehinttype == "and") {
            // this will not work with intersectional 
            results = results || result;
        } else {
            UnsupportedType();
        }
    }
    
    results;
}
GREMLIN;
        return new Command($query, array());
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNo extends DSL {
    public function run(): Command {
        list($property) = func_get_args();

        assert($this->assertProperty($property));

        return new Command('not(has(***))', array($property));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToFunction extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($atoms) = func_get_args();
        } else {
            $atoms = Analyzer::FUNCTIONS_ALL;
        }

        $this->assertAtom($atoms);
        $linksDown = self::$linksDown;
        $diff = $this->normalizeAtoms($atoms);

        $gremlin = <<<GREMLIN
repeat( __.in($linksDown) ).until(hasLabel(within(***)) )
GREMLIN;

        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class ClassDefinition extends DSL {
    public function run(): Command {
        return new Command('in("DEFINITION")');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Has extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($property) = func_get_args();

        assert($this->assertProperty($property));

        return new Command('has(***)', array($property));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasNoClassInterfaceTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run(Analyzer::CIT);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasClassDefinition extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($type) = func_get_args();

        return new Command('where(__.in("DEFINITION").hasLabel(within(***)) )', makeArray($type) );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsLocalClass extends DSL {
    public function run(): Command {
        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
sideEffect{ inside = it.get().value("fullnspath"); }
.where(  __.repeat( __.in({$linksDown}) ).until( hasLabel("Class") ).filter{ it.get().value("fullnspath") == inside; }.count().is(eq(1)) )

GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToParent extends DSL {
    public function run(): Command {
        return new Command('out("EXTENDS").in("DEFINITION")');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class InIs extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = array();
        }

        $this->assertLink($link);

        if (empty($link)) {
            return new Command('in( )');
        }

        $diff = $this->normalizeLinks($link);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        } else {
            return new Command('in(' . $this->SorA($diff) . ')');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class OutIsNot extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 1, 'Too many arguments for ' . __METHOD__);
        list($link) = func_get_args();

        $this->assertLink($link);
        $diff = $this->normalizeLinks($link);

        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        } else {
            return new Command('not( where( __.out(' . $this->SorA($diff) . ')) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasInterfaceDefinition extends DSL {
    public function run(): Command {
        return new Command('where(__.in("DEFINITION").hasLabel("Interface") )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToParameterDefinition extends DSL {
    public function run(): Command {
        return new Command(<<<'GREMLIN'
has("rank")
.choose(
    has("rankName"), 

        // use named parameter to find definition
        __.sideEffect{ranked = it.get().value("rankName");}
        .in('ARGUMENT').optional(__.in("METHOD")).hasLabel("Functioncall", "Newcall", "Methodcall", "Staticmethodcall")
        .in("DEFINITION")
        .out("ARGUMENT")
        .where(__.out("NAME").filter{ it.get().value("fullcode") == ranked; }),

        // default behavior, rank + variadic
        __.sideEffect{ranked = it.get().value("rank");}
                     .in('ARGUMENT').optional(__.in("METHOD")).hasLabel("Functioncall", "Newcall", "Methodcall", "Staticmethodcall")
                     .in("DEFINITION")
                     .out("ARGUMENT")
                     .filter{ (it.get().value("rank") == ranked) || ("variadic" in it.get().keys() && it.get().value("rank") <= ranked); }
)


GREMLIN
);
    }
}

/*

*/
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToAllDefinitions extends DSL {
    public function run(): Command {
        return new Command('emit( ).repeat( __.in("OVERWRITE").not(__.in("PPP").has("visibility", "private")) ).times(' . self::$MAX_LOOPING . ')');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsUsed extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($times) = func_get_args();
        } else {
            $times = 1;
        }

        $gremlin = <<<GREMLIN
where(
    __.out("DEFINITION").hasLabel("Variable", "Variableobject", "Variablearray", "Parameter", "String").count().is(eq($times))
     )
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoUsage extends DSL {
    public function run(): Command {
        return new Command(<<<'GREMLIN'
not(
    where(                  
        __.out("DEFINITION").not(hasLabel("Usenamespace"))
    ) 
)
GREMLIN
);
    }
}
/*

*/

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class AtomIs extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($atoms, $flags) = func_get_args();
                break;

            case 1:
                list($atoms) = func_get_args();
                $flags = Analyzer::WITHOUT_CONSTANTS;
                break;

            default:
                assert(func_num_args() >= 2, 'Too many arguments for ' . __METHOD__);
                assert(func_num_args() === 0, 'No arguments for ' . __METHOD__);
        }

        assert($this->assertAtom($atoms));
        $TIME_LIMIT = self::$TIME_LIMIT;
        $MAX_SEARCHING = self::$MAX_SEARCHING;

        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        } elseif ($flags === Analyzer::WITH_VARIABLES) {
            // arrays, members, static members are not supported
            $gremlin = <<<GREMLIN
union( __.identity(), 
       __.repeat(
            __.timeLimit($TIME_LIMIT).hasLabel("Variable", "Variablearray", "Variableobject", "Phpvariable", "Ternary", "Coalesce", "Parenthesis")
              .union( 
                 // literal local value
                  __.hasLabel("Variable", "Variablearray", "Variableobject", "Phpvariable").in("DEFINITION").hasLabel("Variabledefinition").out("DEFAULT"),

                 // literal value, passed as an argument (Method, closure, function)
                  __.hasLabel("Variable", "Phpvariable").in("DEFINITION").in("NAME").hasLabel('Parameter').as("p1").in("ARGUMENT").out("DEFINITION").optional(__.out("METHOD", "NEW")).out("ARGUMENT").as("p2").where("p1", eq("p2")).by("rank"),

                 // literal value, passed as an argument
                  __.hasLabel("Ternary").out("THEN", "ELSE").not(hasLabel('Void')),

                // \$a ?? 'b'
                  __.hasLabel("Coalesce").out("LEFT", "RIGHT"),

                // (\$a)
                  __.hasLabel("Parenthesis").out("CODE")
                )
            ).times($MAX_SEARCHING).emit()
    )
.hasLabel(within(***))
GREMLIN;
            return new Command($gremlin, array($diff));
        } elseif ($flags === Analyzer::WITH_CONSTANTS) {
            // arrays, members, static members are not supported
            $gremlin = <<<GREMLIN
union( __.identity(), 
       __.repeat(
            __.timeLimit($TIME_LIMIT).hasLabel("Identifier", "Nsname", "Staticconstant", "Variable" , "Ternary", "Coalesce", "Parenthesis", "Functioncall", "Methodcall", "Staticmethodcall")
            .union( __.hasLabel("Identifier", "Nsname", "Staticconstant").in("DEFINITION").out("VALUE"),
            
                      // local variable
                      __.hasLabel("Variable").not(__.in("LEFT").hasLabel("Assignation")).in("DEFINITION").hasLabel('Variabledefinition').has("isConst").optional( __.out("DEFINITION").hasLabel("Staticdefinition")).out("DEFAULT").not(hasLabel("Functioncall", "Methodcall", "Staticmethodcall")),
                      
                      // literal value, passed as an argument (Method, closure, function)
                      __.hasLabel("Variable").in("DEFINITION").in("NAME").hasLabel("Parameter", "Ppp").out("DEFAULT"),
            
                      __.hasLabel("Variable").in("DEFINITION").in("NAME").hasLabel("Parameter", "Ppp").as("p1").timeLimit($TIME_LIMIT).in("ARGUMENT").out("DEFINITION").optional(__.out("METHOD", "NEW")).out("ARGUMENT").as("p2").where("p1", eq("p2")).by("rank"),
            
                      // literal value, passed as an argument
                      __.hasLabel("Ternary").not(__.out("THEN").hasLabel("Void")).out("THEN", "ELSE"),

                      __.hasLabel("Ternary").where(__.out("THEN").hasLabel("Void")).out("CONDITION", "ELSE"),
            
                      __.hasLabel("Coalesce").out("LEFT", "RIGHT"),
            
                      __.hasLabel("Parenthesis").out("CODE"),
            
                      __.hasLabel("Functioncall", "Methodcall", "Staticmethodcall").in('DEFINITION').where( __.out("RETURNTYPE").hasLabel("Void")).out('RETURNED').not(hasLabel("Functioncall", "Methodcall", "Staticmethodcall"))
                      )
            ).times($MAX_SEARCHING).emit()
)
.hasLabel(within(***))
GREMLIN;
            return new Command($gremlin, array($atoms));
        } else {
            // WITHOUT_CONSTANTS or non-constant atoms
            $list = makeList($diff);
            return new Command('hasLabel(' . $list . ')');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoArgumentWithName extends DSL {
    public function run(): Command {
        list($name) = func_get_args();

// Assert parameter name ? It should start with $ and be a variable
//        $this->assertLink($links);

        if (is_array($name)) {
            return new Command('not( where( __.out("ARGUMENT").has("rankName", within(***)) ) )', $name);
        } elseif (is_string($name)) {
            return new Command('not( where( __.out("ARGUMENT").has("rankName", "\\' . $name . '" ) ) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class HasFunctionDefinition extends DSL {
    public function run(): Command {
        return new Command('where( __.in("DEFINITION").hasLabel("Function", "Method", "Closure") )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NotSameTypehintAs extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($variable) = func_get_args();

        $this->assertVariable($variable);

        return new Command('not(where( __.out("RETURNTYPE", "TYPEHINT").has("fullnspath").filter{it.get().value("fullnspath") == ' . $variable . '} ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToAllChildren extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 1:
                list($self) = func_get_args();
                if (!in_array($self, array(Analyzer::INCLUDE_SELF, Analyzer::EXCLUDE_SELF), STRICT_COMPARISON)) {
                    $self = Analyzer::INCLUDE_SELF;
                }
                break;

            default:
                $self = Analyzer::INCLUDE_SELF;
        }

        $MAX_LOOPING = self::$MAX_LOOPING;

        if ($self === Analyzer::EXCLUDE_SELF) {
            $command = new Command(<<<GREMLIN
 as("gotoallchildren")
.repeat( __.out("DEFINITION")
           .in("EXTENDS", "IMPLEMENTS")
           .simplePath().from("gotoallchildren")
          )
          .emit( )
          .times($MAX_LOOPING)
GREMLIN
);
            return $command;
        } else {
            $command = new Command(<<<GREMLIN
 as("gotoallchildren")
.emit( )
.repeat( __.out("DEFINITION")
           .in("EXTENDS", "IMPLEMENTS")
           .simplePath().from("gotoallchildren")
          )
          .times($MAX_LOOPING)
GREMLIN
);
            return $command;
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsNotNullable extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 1:
                list($nullable) = func_get_args();
                $nullable = in_array($nullable, array(IsNullable::EXPLICIT, IsNullable::IMPLICIT), \STRICT_COMPARISON) ? $nullable : IsNullable::IMPLICIT;
                break;

            case 0:
                $nullable = IsNullable::IMPLICIT;
                break;

            default:
                assert(false, 'Wrong number of argument for ' . __METHOD__ . '. 1 is expected, ' . func_num_args() . ' provided');
        }

        if ($nullable === IsNullable::IMPLICIT) {
            return new Command('not(where( __.out("RETURNTYPE", "TYPEHINT").or(
                __.hasLabel("Scalartypehint").has("fullnspath", "\\\\null"),
                __.hasLabel("Null")
                )))');
        } else {
            return new Command('not(where( __.out("RETURNTYPE", "TYPEHINT").hasLabel("Scalartypehint").has("fullnspath", "\\\\null") ) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsInExtendedClass extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($variable) = func_get_args();

        $linksDown = self::$linksDown;

        $this->assertVariable($variable, self::VARIABLE_READ);

        $gremlin = <<<GREMLIN
where(  __.repeat( __.in({$linksDown}) ).until( hasLabel("Class", "Classanonymous") ).filter{ it.get().value("fullnspath") == $variable; } )
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Unique extends DSL {
    public function run(): Command {
        return new Command('unique()');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class PreviousSiblings extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = 'EXPRESSION';
        }

        $hasIn = $this->dslfactory->factory('hasIn');
        $return = $hasIn->run($link);

        return $return->add(new Command('filter{it.get().value("rank") > 0}.sideEffect{sibling = it.get().value("rank");}.in("' . $link . '").out("' . $link . '").filter{sibling + 1 <= it.get().value("rank") }'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasPropertyInside extends DSL {
    public function run(): Command {
        list($property, $values) = func_get_args();

        assert($this->assertProperty($property));
        return new Command('where( __.emit( ).repeat( out(' . self::$linksDown . ') ).times(' . self::$MAX_LOOPING . ').has("' . $property . '", within(***)) )',
                           makeArray($values));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectImplements extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($variable) = func_get_args();
        } else {
            $variable = 'interfaces';
        }

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $MAX_LOOPING = self::$MAX_LOOPING;
        $command = new Command(<<<GREMLIN
where( 
    __.sideEffect{ {$variable} = []; }
      .as("collectimplements")
      .emit( )
      .repeat( __.out("EXTENDS", "IMPLEMENTS")
                 .in("DEFINITION")
                 .hasLabel("Class", "Classanonymous", "Interface")
                 .simplePath().from("collectimplements")
              )
              .times($MAX_LOOPING)
              .hasLabel("Class", "Classanonymous", "Interface")
              .out("EXTENDS", "IMPLEMENTS")
              .sideEffect{ {$variable}.add(it.get().value("fullnspath")) ; }
              .fold() 
)
GREMLIN
);
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectVariables extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 2:
                list($variable, $type) = func_get_args();
                break;

            case 1 :
                list($variable) = func_get_args();
                $type = 'fullcode';
                break;

            default:
                $variable = 'variables';
                $type = 'fullcode';
        }

        assert(in_array($type, array('fullcode', 'code')), 'collectVariable type should be code or fullcode');
        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $containers = array('Variable', 'Variableobject', 'Variablearray', 'Phpvariable', 'Member', 'Staticproperty', 'Array', 'This', );

        return new Command(<<<GREMLIN
sideEffect{ $variable = []; }.where(
    __.repeat( __.outE().hasLabel(within(***)).inV() ).emit()
      .hasLabel(within(***))
      .sideEffect{ 
          $variable.add(it.get().value("$type")); 
      }
      .fold()
)

GREMLIN
, array(self::$LINKS,
        $containers
        )
  );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GroupCount extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($column) = func_get_args();

        $this->assertProperty($column);
        return new Command("groupCount('m').by('$column')");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class FunctionInside extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        // $fullcode is a name of a variable
        $gremlin = 'emit( ).repeat( __.out(' . self::$linksDown . ').not(hasLabel("Closure", "Classanonymous", "Function", "Class", "Trait")) ).times(' . self::$MAX_LOOPING . ').hasLabel("Functioncall").has("fullnspath", within(***))';
        return new Command($gremlin, array(makeArray($fullnspath)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotMixedcase extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($property) = func_get_args();
        } else {
            $property = 'fullcode';
        }

        assert($this->assertProperty($property));
        return new Command('filter{it.get().value("' . $property . '") == it.get().value("' . $property . '").toString().toLowerCase() || it.get().value("' . $property . '") == it.get().value("' . $property . '").toString().toUpperCase()}');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GetWs extends DSL {
    public function run(): Command {
        list($values) = func_get_args();

        // @todo : retrieve a single element from an array ["x"][0]
        // @todo check the elements from WS first
        // @todo : initialize the variables
        $code = array();
        $arguments = array();
        foreach($values as $element => $variable) {
            $code[] = "$variable = j[\"$element\"];";
        }

        $codeGremlin = implode(PHP_EOL, $code);

        $gremlin = <<<GREMLIN
sideEffect{ 
    json = new groovy.json.JsonSlurper();
    j = json.parseText(it.get().property("ws").value().toString());
    $codeGremlin
    
    it.get().property("ws", groovy.json.JsonOutput.toJson(j));
}
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class AnalyzerIsNot extends DSL {
    public function run(): Command {
        list($analyzer) = func_get_args();

        $analyzer = makeArray($analyzer);

        if (($id = array_search('self', $analyzer)) !== false) {
            $analyzer[$id] = $this->analyzerQuoted;
        }
        $analyzer = array_map(Analyzer::class . '::getName', $analyzer);

        assert($this->assertAnalyzer($analyzer));

        return new Command('not( where( __.in("ANALYZED").has("analyzer", within(***))) )', array($analyzer));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class NoDelimiterIs extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 2, 'Too many arguments for ' . __METHOD__);

        switch (func_num_args()) {
            case 2:
                list($code, $caseSensitive) = func_get_args();
                break;

            case 1:
                list($code) = func_get_args();
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            default:
                assert(false, 'No enought arguments for ' . __METHOD__);
        }

        $return = new Command('has("noDelimiter")');
        $propertyIs = $this->dslfactory->factory('propertyIs');
        $code = makeArray($code);

        return $return->add($propertyIs->run('noDelimiter', $code, $caseSensitive));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class FollowAlias extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 1:
                $loopings = (int) func_get_arg(0);
                break;

           default:
                $loopings = self::$MAX_LOOPING;
                break;
        }

        // Coalesce is not supported
        return new Command(<<<GREMLIN
emit().repeat(
    __.out("DEFINITION").in("DEFAULT").hasLabel("Variabledefinition")
).times($loopings)
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasIn extends DSL {
    public function run(): Command {
        list($links) = func_get_args();

        assert($this->assertLink($links));
        $diff = $this->normalizeLinks($links);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        } else {
            return new Command('where( __.in(' . $this->SorA($diff) . ') )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Dedup extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($by) = func_get_args();
        } else {
            $by = '';
        }

        if (empty($by)) {
            return new Command('dedup()');
        }

        if (is_array($by)) {
            foreach($by as $b) {
                assert($this->assertLabel($b));
            }
            return new Command('dedup(' . makeList($by) . ')');
        }

        assert($this->assertProperty($by));
        return new Command("dedup().by('$by')");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoVariadicArgument extends DSL {
    public function run(): Command {
        return new Command('not(where(__.out("ARGUMENT").has("variadic", true)))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasNoLoop extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run(Analyzer::LOOPS_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToLiteralValue extends DSL {
    public function run(): Command {
        return new Command('coalesce(__.in("DEFINITION").in("NAME").out("VALUE"), 
                                     __.in("DEFINITION").out("VALUE"), 
                                        __.filter{ true; })');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class IsHash extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 3 :
                list($property, $hash, $index) = func_get_args();
                $case = Analyzer::CASE_SENSITIVE;
                break;

            case 4 :
                list($property, $hash, $index, $case) = func_get_args();
                assert(in_array($case, array(Analyzer::CASE_INSENSITIVE, Analyzer::CASE_SENSITIVE)));
                break;

            default:
                assert(false, 'Wrong number of arguments for ' . __METHOD__);
        }

        if (empty($hash)) {
            return new Command(Query::STOP_QUERY);
        }

        assert($this->assertProperty($property));

        if ($case === Analyzer::CASE_INSENSITIVE) {
            return new Command("has(\"$property\").filter{ x = ***[$index]; x != null; }.filter{ [it.get().value(\"$property\").toLowerCase()].intersect(x) != []; }", array($hash));
        } else {
            return new Command("has(\"$property\").filter{ x = ***[$index]; x != null; }.filter{ [it.get().value(\"$property\")].intersect(x) != []}", array($hash));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GroupFilter extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($characteristic, $percentage) = func_get_args();

        if (substr(trim($characteristic), 0, 3) === 'it.') {
            $by = "by{ $characteristic }";
        } else {
            $by = "by{ \"$characteristic\" }";
        }

        $gremlin = <<<GREMLIN
groupCount("gf").$by.cap("gf").sideEffect{ s = it.get().values().sum(); }.next().findAll{ it.value < s * $percentage; }.keySet()
GREMLIN;
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class RegexIs extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($property, $regex) = func_get_args();

        $this->assertProperty($property);

        if ($property === 'code') {
            $values = $this->dictCode->grep($regex);

            if (empty($values)) {
                return new Command(Query::NO_QUERY);
            }

            return new Command('has("code", within(***) )', array($values));
        }

        return new Command(<<<GREMLIN
has("$property")
.filter{ (it.get().value("$property") =~ "$regex").asBoolean(); }
GREMLIN
                          );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasFunction extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run(Analyzer::FUNCTIONS_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class FunctionDefinition extends DSL {
    public function run(): Command {
        return new Command('in("DEFINITION").hasLabel("Function", "Method", "Magicmethod", "Closure")');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class ToDrop extends DSL {
    public function run(): Command {
        return new Command('property("toDrop", true)');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Raw extends DSL {
    public function run(): Command {
        $args = func_get_args();
        $query = array_shift($args);

        $query = trim($this->cleanAnalyzerName($query, $this->dependsOn));
        assert($query[0] !== '.', 'Raw() step shall not start with a . in the Gremlin Code');

        return new Command($query, $args);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;
use Exakat\Query\Query;

class FullnspathIs extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($code, $caseSensitive) = func_get_args();
        } else {
            list($code) = func_get_args();
            $caseSensitive = Analyzer::CASE_INSENSITIVE;
        }

        if (empty($code)) {
            return new Command(Query::NO_QUERY);
        }

        $has = $this->dslfactory->factory('has');
        $return = $has->run('fullnspath');

        $propertyIs = $this->dslfactory->factory('propertyIs');
        $code = makeArray($code);

        return $return->add($propertyIs->run('fullnspath', $code, $caseSensitive));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoFullcodeInside extends DSL {
    public function run(): Command {
        list($fullcode) = func_get_args();

        // $fullcode is a name of a variable
        $gremlin = 'not( where( __.emit( ).repeat( out(' . self::$linksDown . ') ).times(' . self::$MAX_LOOPING . ').filter{ it.get().value("fullcode") == ' . $fullcode . '}) )';
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoClassInterface extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run(array('Class', 'Classanonymous', 'Interface'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoIfthen extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run('Ifthen');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class OutIs extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 1, 'Too many arguments for ' . __METHOD__);

        if (func_num_args() == 0) {
            return new Command('out( )');
        }

        list($link) = func_get_args();

        if (empty($link)) {
            return new Command('out( )');
        }

        $this->assertLink($link);
        $diff = $this->normalizeLinks($link);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        } else {
            return new Command('out(' . $this->SorA($diff) . ')');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class PreviousSibling extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = 'EXPRESSION';
        }

        $hasIn = $this->dslfactory->factory('hasIn');
        $return = $hasIn->run($link);

        return $return->add(new Command('sideEffect{sibling = it.get().value("rank");}.in("' . $link . '").out("' . $link . '").filter{sibling - 1 == it.get().value("rank")}'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class InIsIE extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($links) = func_get_args();

        assert($this->assertLink($links));

        $diff = $this->normalizeLinks($links);
        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        } else {
            return new Command('until(__.not(__.inE(' . $this->SorA($diff) . '))).repeat(__.in(' . $this->SorA($diff) . '))');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoUseDefinition extends DSL {
    public function run(): Command {
        return new Command('not( where(__.out("DEFINITION").in("USE").hasLabel("Use")) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class FunctioncallIs extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $this->assertArguments(1, func_num_args(), __METHOD__);
        assert($fullnspath !== null, 'fullnspath can\'t be null in ' . __METHOD__);

        $diff = $this->normalizeFunctioncalls($fullnspath);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $atomIs = $this->dslfactory->factory('atomIs');
        $return = $atomIs->run('Functioncall', Analyzer::WITHOUT_CONSTANTS);

        $has = $this->dslfactory->factory('has');
        $return->add($has->run('fullnspath'));

        $fullnspathIs = $this->dslfactory->factory('fullnspathIs');
        $return->add($fullnspathIs->run(array_values($diff), Analyzer::CASE_INSENSITIVE));

        return $return;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsAnyOf extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($properties, $value) = func_get_args();

        assert(is_array($properties), 'Property argument should be an array. ' . gettype($properties) . " was provided\n");
        foreach($properties as $property) {
            $this->assertProperty($property);
        }

        $command = array();
        if ($value === null) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", null)';
            }
/*        } elseif (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
            $value = $value === true ? 'true' : 'false';

            return new Command('filter{ if ( it.get().properties("' . $property . '").any()) { ' . $value . ' == it.get().value("' . $property . '")} else {' . $value . ' == false; }; }');
        */} elseif ($value === true) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", true)';
            }
        } elseif ($value === false) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", false)';
            }
        } elseif (is_int($value)) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", ' . $value . ')';
            }
        } elseif (is_string($value)) {
            assert(false, 'Not done yet');
            return new Command('has("' . $property . '", ***)', array($value));
        } elseif (is_array($value)) {
            assert(false, 'Not done yet');
            return new Command('has("' . $property . '", within(***))', array($value));
        } else {
            assert(false, 'Not understood type for is : ' . gettype($value));
        }

        return new Command('or(' . join(', ', $command) . ')');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CodeIsPositiveInteger extends DSL {
    public function run(): Command {
        return new Command('filter{ if( it.code.isInteger()) { it.code > 0; } else { true; }}');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasVariadicArgument extends DSL {
    public function run(): Command {
        return new Command('where(__.out("ARGUMENT").has("variadic", true))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasParent extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($parentClass, $ins) = func_get_args();

        if (empty($ins)){
            return new Command(Query::NO_QUERY);
        }

        $this->assertAtom($parentClass);
        $this->assertLink($ins);
        $diff = $this->normalizeAtoms($parentClass);

        if (empty($diff)){
            return new Command(Query::STOP_QUERY);
        }

        $in = $this->makeLinks($ins, 'in');

        return new Command("where( __$in.hasLabel(within(***)))", array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasLoop extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run(Analyzer::LOOPS_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class SetWs extends DSL {
    public function run(): Command {
        list($values) = func_get_args();

        // @todo : set a array (j["opening"][0] = 1)
        // @todo : set a piece of code or a relative value  (                        j["opening"] = j["opening"].substring(3, j["opening"].length());)
        // @todo : get a piece of code
        // @todo : remove a piece of code (j["closing"].remove())
        $code = array();
        $arguments = array();
        foreach($values as $name => $value) {
            $code[] = "j[\"$name\"] = ***;";
            $arguments[] = $value;
        }

        $codeGremlin = implode(PHP_EOL, $code);

        $gremlin = <<<GREMLIN
sideEffect{ 
    json = new groovy.json.JsonSlurper();
    j = json.parseText(it.get().property("ws").value().toString());
    $codeGremlin
    
    it.get().property("ws", groovy.json.JsonOutput.toJson(j));
}
GREMLIN;

        return new Command($gremlin, $arguments);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class IsComplexExpression extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($threshold) = func_get_args();
        } else {
            $threshold = 30;
        }

        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
not( where( __.has("constant", true ) ) )
.where(
  __.emit().repeat( __.not(hasLabel(within(***)) ).out({$linksDown})).times(***.intValue())
    .not(hasLabel(within(***)) )
    .count().is(gt(***)) 
)

GREMLIN;
        return new Command($gremlin, array(Analyzer::ANONYMOUS, self::$MAX_LOOPING, Analyzer::ANONYMOUS, $threshold));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class PreviousCalls extends DSL {
    public function run(): Command {

        if(func_num_args() === 1) {
            $times = abs((int) func_get_arg(0));
        } else {
            $times = 1;
        }
        // Starting from Parameter, going to next parameter
        // Need a number of executions?

        if ($times === 0) {
            return new Command(Query::NO_QUERY);
        } else {
            $TIME_LIMIT = self::$TIME_LIMIT;

            return new Command(<<<GREMLIN
emit().repeat(
     __
     .timeLimit($TIME_LIMIT)
     .sideEffect{ ranked = it.get().value('rank');}
     .in('ARGUMENT')
     .out('DEFINITION')
     .out('ARGUMENT')
     .filter{ it.get().value('rank') == ranked;}
     .in('DEFINITION')
     .in("NAME")

).times($times)
GREMLIN
);
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Exceptions\DSLException;
use Exakat\Tasks\Helpers\Atom;
use Exakat\GraphElements;
use Exakat\Analyzer\Analyzer;

abstract class DSL {
    public const VARIABLE_WRITE = true;
    public const VARIABLE_READ  = false;

    public const LABEL_SET  = true;
    public const LABEL_GO   = false;

    public const LEVELS_TO_ANALYSE = 4;

    public const PROPERTIES = array('id',
                             'atom',
                             'code',
                             'lccode',
                             'fullcode',
                             'line',
                             'token',
                             'rank',
                             'alternative',
                             'reference',
                             'heredoc',
                             'delimiter',
                             'noDelimiter',
                             'variadic',
                             'count',
                             'fullnspath',
                             'absolute',
                             'alias',
                             'origin',
                             'encoding',
                             'block',
                             'intval',
                             'strval',
                             'boolean',
                             'enclosing',
                             'args_max',
                             'args_min',
                             'bracket',
                             'flexible',
                             'close_tag',
                             'aliased',
                             'propertyname',
                             'constant',
                             'root',
                             'globalvar',
                             'binaryString',
                             'isNull',
                             'visibility',
                             'final',
                             'abstract',
                             'static',
                             'noscream',
                             'trailing',
                             'isPhp',
                             'isExt',
                             'isStub',
                             'typehint',
                             );

    protected const BOOLEAN_PROPERTY = array('abstract',
                                             'absolute',
                                             'aliased',
                                             'alternative',
                                             'bracket',
                                             'constant',
                                             'enclosing',
                                             'final',
                                             'heredoc',
                                             'isModified',
                                             'isRead',
                                             'noscream',
                                             'reference',
                                             'static',
                                             'trailing',
                                             'variadic',
                                             'isPhp',
                                             'isExt',
                                             'isStub',
                                             );

    protected const INTEGER_PROPERTY = array('line',
                                             'rank',
                                             'propertyname',
                                             'boolean',
                                             'count',
                                             'code',
                                             'lccode',
                                             );

    protected $dslfactory             = null;
    protected $availableAtoms         = array();
    protected $availableLinks         = array();
    protected $availableFunctioncalls = array();
    protected $availableVariables     = array(); // This one is per query
    protected $availableLabels        = array(); // This one is per query
    protected $dictCode               = null;
    protected $ignoredcit             = null;
    protected $ignoredfunctions       = null;
    protected $ignoredconstants       = null;
    protected $dependsOn              = array();
    protected $analyzerQuoted         = '';

    protected static $linksDown     = '';
    protected static $MAX_LOOPING   = Analyzer::MAX_LOOPING;
    protected static $MAX_SEARCHING = Analyzer::MAX_SEARCHING;
    protected static $TIME_LIMIT    = Analyzer::TIME_LIMIT;
    protected static $ATOMS         = array();
    protected static $LINKS         = array();

    public function __construct(DSLFactory $dslfactory,
                                array $availableAtoms         = array(),
                                array $availableLinks         = array(),
                                array $availableFunctioncalls = array(),
                                array &$availableVariables    = array(),
                                array &$availableLabels       = array(),
                                array $ignoredcit             = array(),
                                array $ignoredfunctions       = array(),
                                array $ignoredconstants       = array(),
                                array $dependsOn              = array(),
                                string $analyzerQuoted        = ''
                                ) {
        $this->dslfactory             = $dslfactory;
        $this->dictCode               = exakat('dictionary');
        $this->availableAtoms         = $availableAtoms;
        $this->availableLinks         = $availableLinks;
        $this->availableFunctioncalls = $availableFunctioncalls;
        $this->availableVariables     = &$availableVariables;
        $this->availableLabels        = &$availableLabels;
        $this->ignoredcit             = $ignoredcit;
        $this->ignoredfunctions       = $ignoredfunctions;
        $this->ignoredconstants       = $ignoredconstants;
        $this->dependsOn              = $dependsOn;
        $this->analyzerQuoted         = $analyzerQuoted;

        if (empty(self::$linksDown)) {
            self::$linksDown = GraphElements::linksDownAsList();
        }

        if (empty(self::$ATOMS)) {
            self::$ATOMS = array_merge(GraphElements::$ATOMS, GraphElements::$ATOMS_EXAKAT);
        }

        if (empty(self::$LINKS)) {
            self::$LINKS = array_merge(GraphElements::$LINKS, GraphElements::$LINKS_EXAKAT);
        }
    }

    abstract public function run(): Command;

    protected function normalizeAtoms($atoms): array {
        $atoms = makeArray($atoms);
        return array_values(array_intersect($atoms, $this->availableAtoms));
    }

    protected function normalizeLinks($links): array {
        $links = makeArray($links);
        return array_values(array_intersect($links, $this->availableLinks));
    }

    protected function normalizeFunctioncalls($fullnspaths): array {
        $fullnspaths = makeArray($fullnspaths);
        return array_values(array_intersect($fullnspaths, $this->availableFunctioncalls));
    }

    protected function SorA($value): string {
        if (is_array($value)) {
            return makeList($value);
        } elseif (is_string($value)) {
            return '"' . $value . '"';
        }
        assert(false, '$v is not a string or an array');
    }

    protected function assertLabel($name, bool $read = self::LABEL_GO): bool {
        if (is_array($name)) {
            foreach($name as $n) {
                $this->assertLabel($n, $read);
            }
            return true;
        }

        if ($read === self::LABEL_SET) {
            assert(!in_array($name, $this->availableLabels), "Label '$name' is already set : " . join(', ', $this->availableLabels));
            $this->availableLabels[] = $name;
        } else {
            assert(in_array($name, $this->availableLabels), "Label '$name' is not set");
        }
        return true;
    }

    protected function isVariable(string $name): bool {
        return in_array($name, $this->availableVariables);
    }

    protected function assertVariable(string $name, bool $write = self::VARIABLE_READ): bool {
        if ($write === self::VARIABLE_WRITE) {
            assert(!$this->isVariable($name), "Variable '$name' is already taken : " . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), true) . PHP_EOL . print_r($this, true));
            assert(!in_array($name, self::PROPERTIES), "Don't use a property name as a variable ($name)");
            $this->availableVariables[] = $name;
        } else {
            assert($this->isVariable($name), "Variable '$name' is not defined");
        }
        return true;
    }

    protected function assertLink($link): bool {
        if (is_string($link)) {
            if(in_array($link, array('KEY', 'ELEMENT', 'PROPERTY')) ) {
                throw new DSLException("$link is no more", self::LEVELS_TO_ANALYSE);
            }
            if($link !== strtoupper($link)) {
                throw new DSLException("Wrong format for LINK name : $link", self::LEVELS_TO_ANALYSE);
            }
            if(preg_match('/[^A-Z]/', $link)) {
                throw new DSLException("Not a link : $link", self::LEVELS_TO_ANALYSE);
            }
            assert(in_array($link, self::$LINKS), "No such link as '$link'");
        } elseif (is_array($link)) {
            foreach($link as $l) {
                $this->assertLink($l);
                assert(in_array($l, self::$LINKS), "No such link as '$l'");
            }
        } else {
            assert(false, 'Unsupported type for link : ' . gettype($link));
        }
        return true;
    }

    protected function assertTokens($token): bool {
        if (is_string($token)) {
            assert(substr($token, 0, 2) === 'T_', "Wrong prefix for TOKEN name : $token");
            assert($token === strtoupper($token), "Wrong format for TOKEN name : $token");
        } elseif (is_array($token)) {
            foreach($token as $t) {
                assert(substr($t, 0, 2) === 'T_', "Wrong prefix for TOKEN name : $t");
                assert($t === strtoupper($t), "Wrong format for TOKEN name : $t");
            }
        } else {
            assert(false, 'Unsupported type for token : ' . gettype($token));
        }
        return true;
    }

    protected function assertAtom($atom): bool {
        if (is_string($atom)) {
            assert($atom === ucfirst(strtolower($atom)), "Wrong format for Atom name : $atom");
            assert(in_array($atom, self::$ATOMS), "No such atom as '$atom'");
        } elseif (is_array($atom)) {
            foreach($atom as $a) {
                assert($a === ucfirst(strtolower($a)), "Wrong format for Atom name : $a");
                assert(in_array($a, self::$ATOMS), "No such atom as '$a'");
            }
        } else {
            assert(false, 'Unsupported type for atom : ' . gettype($atom));
        }

        return true;
    }

    protected function assertAnalyzer($analyzer): bool {
        if (is_string($analyzer)) {
            assert(preg_match('#^[A-Z]\w+/[A-Z]\w+$#', $analyzer) !== false, "Wrong format for Analyzer : $analyzer");
            assert(class_exists('\\Exakat\\Analyzer\\' . str_replace('/', '\\', $analyzer)), "No such analyzer as $analyzer");
        } elseif (is_array($analyzer)) {
            foreach($analyzer as $a) {
                assert(preg_match('#^[A-Z]\W\w+/[A-Z]\W\w+$#', $a) !== false, "Wrong format for Analyzer : $a");
                assert(class_exists('\\Exakat\\Analyzer\\' . str_replace('/', '\\', $a)), "No such analyzer as $a");
            }
        } else {
            assert(false, 'Unsupported type for analyzer : ' . gettype($analyzer));
        }

        return true;
    }

    protected function isProperty($property): bool {
        return property_exists(Atom::class, $property) || in_array($property, array('typehint', 'label', 'self', 'ignored_dir', 'virtual', 'analyzer', 'propagated', 'isPhp', 'isExt', 'isStub'));
    }

    protected function assertProperty($property): bool {
        if (is_string($property)) {
            assert( ($property === mb_strtolower($property)) || in_array($property, array('noDelimiter', 'isRead', 'isModified', 'isPhp', 'isExt', 'isStub', 'rankName')) , 'Wrong format for property name : "' . $property . '"');
            assert($this->isProperty($property), 'No such property in Atom : "' . $property . '"');
        } elseif (is_array($property)) {
            $properties = $property;
            foreach($properties as $property) {
                assert( ($property === mb_strtolower($property)) || in_array($property, array('noDelimiter', 'isRead', 'isModified', 'isPhp', 'isExt', 'isStub', )), "Wrong format for property name : '$property'");
                assert($this->isProperty($property), "No such property in Atom : '$property'");
            }
        } else {
            assert(false, 'Unsupported type for property : ' . gettype($property));
        }
        return true;
    }

    protected function cleanAnalyzerName(string $gremlin, array $dependencies = array()): string {
        $fullNames = array_map(array($this, 'makeBaseName'), $dependencies);

        return str_replace($dependencies, $fullNames, $gremlin);
    }

    public static function makeBaseName(string $className): string {
        // No Exakat, no Analyzer, using / instead of \
        return $className;
    }

    protected function tolowercase($code) {
        if (is_array($code)) {
            $code = array_map('mb_strtolower', $code);
        } elseif (is_scalar($code)) {
            $code = mb_strtolower($code);
        } else {
            assert(false, __METHOD__ . ' received an unprocessable object ' . gettype($code));
        }

        return $code;
    }

    protected function makeLinks($links, string $direction = 'in'): string {
        if (empty($links)) {
            return '.out( )';
        }

        $return = array();

        $links = makeArray($links);
        foreach($links as $l) {
            if (empty($l)) {
                $return[] = ".$direction( )";
            } elseif (is_array($l)) {
                $list = implode('", "', $l);
                $return[] = ".$direction(\"$list\")";
            } elseif (is_string($l)) {
                $return[] = ".$direction(\"$l\")";
            } else {
                assert(false, __METHOD__ . ' received an unprocessable object ' . gettype($l));
            }
        }

        return implode('', $return);
    }

    protected function assertArguments(int $maxCount, int $actualCount, string $method): void {
        assert($actualCount === $maxCount,
            'Wrong number of argument for ' . $method . '. ' . $maxCount . ' is expected, ' . $actualCount . ' provided.');
    }

    protected function protectValue($value): string {
        if (is_string($value)) {
            if ($this->isVariable($value)) {
                return $value;
            } else {
                return '"' . addslashes($value) . '"';
            }
        } elseif (is_int($value)) {
            return (string) $value;
        } elseif (is_null($value)) {
            return 'null';
        } elseif (is_bool($value)) {
            return $value ? 'true' : 'false';
        } else {
            assert(false, 'Could not process value type : ' . get_type($value));
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoConstantDefinition extends DSL {
    public function run(): Command {
        return new Command('not(where( __.in("DEFINITION").hasLabel("Constant", "Defineconstant") ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IdenticalTo extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($variable) = func_get_args();

        $this->assertLabel($variable);

        return new Command("where(eq('$variable'))");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasChildren extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($childrenClass, $outs) = func_get_args();

        if (empty($outs)){
            return new Command(Query::NO_QUERY);
        }

        $this->assertAtom($childrenClass);
        $diff = $this->normalizeAtoms($childrenClass);
        if (empty($diff)){
            return new Command(Query::NO_QUERY);
        }

        $out = $this->makeLinks($outs, 'out');

        return new Command("where( __$out.hasLabel(within(***)) )", array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotLiteral extends DSL {
    public function run(): Command {
        // Closures are literal if not using a variable from the context
        return new Command(<<<'GREMLIN'
not( 
    __.hasLabel("Integer", "Boolean", "Null", "Magicconstant", "Float", "String", "Heredoc", "Closure", "Arrayliteral")
      .has("constant", true) 
)

GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasNoInstruction extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($atoms) = func_get_args();
        } else {
            $atoms = 'Namespaces';
        }

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        }

        $stop = array('File');
        $stop = array_unique(array_merge($stop, $diff));

        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
not( 
    where( 
         __.emit( ).repeat(__.in({$linksDown}))
                   .until(hasLabel(within(***)))
                   .hasLabel(within(***))
         ) 
    )
GREMLIN;
        return new Command($gremlin, array($stop, $diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class FollowExpression extends DSL {
    public function run(): Command {
        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

        // Coalesce is not supported
        return new Command(<<<GREMLIN
emit( ).repeat( 
    __.coalesce(__.out("THEN", "ELSE", "CODE"), 
                __.filter{true})
      .out({$linksDown}) 
      )
.times($MAX_LOOPING) 
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsPropertyDefined extends DSL {
    public function run(): Command {
        // check for DEFINTION link and the Propertydefinition atom
        return new Command('where( __.in("DEFINITION").hasLabel("Propertydefinition"))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToArray extends DSL {
    public function run(): Command {
        return new Command('emit( ).repeat( __.in("VARIABLE", "INDEX")).until( where(__.in("VARIABLE", "INDEX").hasLabel("Array").count().is(eq(0)) ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoInterface extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');
        return $return->run('Interface');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class ProcessIndentingAverage extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 is expected, ' . func_num_args() . ' provided');

        $indentationThreshold = abs((float) func_get_arg(0));
        $minimumSize          = abs((int) func_get_arg(1));

        if ($indentationThreshold === 0) {
            return new Command(Query::NO_QUERY);
        }

        $MAX_LOOPING = self::$MAX_LOOPING;

        // round() is used for lone blocks in the code
        // it may be excessive
        $command = new Command(<<<GREMLIN
where(
    __.sideEffect{ levels = []; }
      .repeat( __.out("BLOCK", "EXPRESSION", "THEN", "ELSE", "CASES")).emit().times($MAX_LOOPING)
      .not(hasLabel("Sequence", "Block", "Void"))
      .path()
      .sideEffect{ levels.add(Math.round((it.get().size() - 1 ) / 2 - 1)); }
      .fold()
      .filter{ levels != [];}
).filter{ levels.size() >= $minimumSize && levels.sum() / levels.size() >= $indentationThreshold}
GREMLIN
);

        return $command;
    }
}

/* debugging purposes
.sideEffect{ name = it.get().value('fullcode'); }
.where(
    __.sideEffect{ levels = []; fullcodes = [];}
      .repeat( __.out('BLOCK', 'EXPRESSION', 'THEN', 'ELSE', 'CASES')).emit().times(100)
      .not(hasLabel('Sequence', 'Block', "Void"))
      .sideEffect{ fullcodes.add(it.get().value('fullcode'));}
      .path()
      .sideEffect{ levels.add(Math.round((it.get().size() - 1 ) / 2 - 1)); }
      .fold()
)
//.filter{ levels.sum() / levels.size() > 1}
.map{['name':name, 'levels':levels, 'average':levels.sum() / levels.size(), 'max':levels.max(), 'fullcode':fullcodes ];}

*/
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasTraitDefinition extends DSL {
    public function run(): Command {
        return new Command('where(__.in("DEFINITION").hasLabel("Trait") )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Trim extends DSL {
    public function run(): Command {
        switch (func_get_args()) {
            case 2:
                list($variable, $chars) = func_get_args();
                break 1;

            case 1:
                $variable = func_get_arg(0);
                $chars = '\'\"';
                break 1;

            default:
                assert(false, 'Not enough arguments for ' . __METHOD__);
        }

        $this->assertVariable($variable);

        return new Command('sideEffect{' . $variable . '.replaceFirst("^[' . $chars . ']?(.*?)[' . $chars . ']?\$", "\$1"); }');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToOtherBranch extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($links, $label) = func_get_args();
                assert(count($links) > 1, 'No enough options for branches');
                if (empty($label)) {
                    $as = '';
                } else {
                    assert($this->assertLabel($label, self::LABEL_SET));
                    $as = '.as("' . $label . '")';
                }
                break;

            case 1:
                list($links) = func_get_args();
                $as = '';
                break;

            default:
                throw new Exception('Wrong number of arguments for goToOtherBranch : ' . func_num_args() . ' provided, 1 or 2 expected.');

        }

            return new Command('inE().hasLabel(within(***)).as("b1").outV()' . $as . '.outE().hasLabel(within(***)).as("b2").where("b1", neq("b2")).by(label).inV()',
            array($links, $links),
        );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class OutIsIE extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($links) = func_get_args();
        assert($this->assertLink($links));

        $diff = $this->normalizeLinks($links);
        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        }

        return new Command('until( __.not(outE(' . $this->SorA($diff) . ')) ).repeat(out(' . $this->SorA($diff) . '))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class PropertyIsNot extends DSL {
    public function run(): Command {
        list($property, $code, $caseSensitive) = func_get_args();

        assert($this->assertProperty($property));

        if (is_array($code) && empty($code) ) {
            return new Command(Query::NO_QUERY);
        }

        if ($caseSensitive === Analyzer::CASE_SENSITIVE) {
            $caseSensitive = '';
        } elseif ($caseSensitive === Analyzer::CASE_INSENSITIVE) {
            $code = $this->tolowercase($code);
            $caseSensitive = '.toString().toLowerCase()';
        } else {
            assert(false, 'No such case sensitivity : "' . $caseSensitive . '"');
        }

        if (is_array($code) && !empty(array_intersect($code, $this->availableVariables))) {
            return new Command('filter{ !("' . $property . '" in it.get().keys()) || it.get().value("' . $property . '")' . $caseSensitive . ' != ' . $code[0] . '}', array());
        } elseif (is_string($code) && in_array($code, $this->availableVariables)) {
            return new Command(<<<GREMLIN
filter{
    if ($code instanceof java.util.List) {
        !(it.get().value("$property")$caseSensitive in $code);
    } else {
        it.get().value("$property")$caseSensitive != $code;
    }
}
GREMLIN
, array());
        } elseif (is_array($code)) {
            return new Command('filter{ !("' . $property . '" in it.get().keys()) || !(it.get().value("' . $property . '")' . $caseSensitive . ' in ***); }', array($code));
        } else {
            return new Command('filter{!("' . $property . '" in it.get().keys()) || (it.get().value("' . $property . '")' . $caseSensitive . ' != ***)}', array($code));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotInheritedMethod extends DSL {
    public function run(): Command {
        $gremlin = <<<'GREMLIN'
not( 
    where( __.out("OVERWRITE")
             .not( where( __.has("abstract", true) ) ) 
             .not( where( __.has("visibility", "private") ) ) 
             .not( where( __.in("METHOD", "MAGICMETHOD").hasLabel("Interface") ) ) 
    )
)
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToParameterUsage extends DSL {
    public function run(): Command {
        return new Command(<<<'GREMLIN'

sideEffect{
    ranked   = it.get().value("rank");
    variadic = "variadic" in it.get().keys();
}
.sideEffect( __.out("NAME").sideEffect{rankedName = it.get().value("fullcode");})
.in('ARGUMENT')
.out('DEFINITION')
.optional( __.out("METHOD"))
.out('ARGUMENT')
.filter{it.get().value("rank") == ranked || ("rankName" in it.get().keys() && it.get().value("rankName") == rankedName) || (variadic == true && it.get().value("rank") >= ranked);}
.not(hasLabel("Void"))
GREMLIN
);

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NotExtending extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $MAX_LOOPING = self::$MAX_LOOPING;
        return new Command(<<<GREMLIN
not(
    where( __.emit().repeat( __.out("EXTENDS").in("DEFINITION")).times($MAX_LOOPING)
                    .out("EXTENDS").has("fullnspath", within(***)) ) 
)
GREMLIN
, array($fullnspath));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Exceptions\WrongNumberOfArguments;

class AddEFrom extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($edgeName, $from) = func_get_args();
            $options = array();
        } elseif (func_num_args() === 3) {
            list($edgeName, $from, $options) = func_get_args();
            assert(is_array($options), 'Edge options must be an array');
        } else {
            throw new WrongNumberOfArguments(__METHOD__, func_num_args(), 3);
        }

        assert($this->assertLabel($from, self::LABEL_GO));

        $properties = array();
        foreach ($options as $name => $value) {
            $properties[] = ".property(\"$name\", $value)";
        }
        $properties = implode('', $properties);

        return new Command("addE(\"$edgeName\").from(\"$from\")$properties");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class SaveOutAs extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 3:
                list($name, $out, $sort) = func_get_args();
                break 1;

            case 2:
                list($name, $out) = func_get_args();
                $sort = 'rank';
                break 1;

            case 1:
                list($name) = func_get_args();
                $sort = 'rank';
                $out = 'ARGUMENT';
                break 1;

            default:
                assert(false, 'Wrong number of argument for ' . __METHOD__ . '. 1 to 3 are expected, ' . func_num_args() . ' provided');
        }

        // Calculate the arglist, normalized it, then put it in a variable
        // This needs to be in Arguments, (both Functioncall or Function)
        if (empty($sort)) {
            $sortStep = '';
        } else {
            $sortStep = ".sort{it.value(\"$sort\")}";
        }

        $check = $this->dslfactory->factory('initVariable');
        $return = $check->run($name);

        $gremlin = <<<GREMLIN
sideEffect{ 
    s = [];
    it.get().vertices(OUT, "$out")$sortStep.each{ 
        s.push(it.value('code'));
    };
    $name = s.join(', ');
    true;
}

GREMLIN;
        return $return->add(new Command($gremlin));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasClassTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run(Analyzer::CLASSES_TRAITS);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class FullcodeLength extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($length) = func_get_args();

        return new Command('filter{it.get().value("fullcode").length() ' . $length . '}');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsVisible extends DSL {
    public const VISIBLE_ABOVE = 1;
    public const VISIBLE_BELOW = 2;
    public const ALL_VISIBLE   = array(self::VISIBLE_ABOVE, self::VISIBLE_BELOW);

    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($variable, $by) = func_get_args();
                break;

            case 1:
                list($variable) = func_get_args();
                $by = self::VISIBLE_ABOVE;
                break;

            default:
                assert(false, 'wrong number of argument for ' . __METHOD__);
        }

        $this->assertVariable($variable, self::VARIABLE_READ);

        if (!in_array($by, self::ALL_VISIBLE)) {
            $by = self::VISIBLE_ABOVE;
        }

        if ($by === self::VISIBLE_ABOVE) {
            // The incoming variable is located above the current one
            // This is covariant :
            $return = new Command(<<<GREMLIN
filter{ 
    if (it.get().properties("visibility").any()) { 
        if ($variable == "private") {
            it.get().value("visibility") in ["private", "protected", "public", "none"];
        } else if ($variable == "protected") {
            it.get().value("visibility") in ["protected", "public", "none"];
        } else if ($variable == "public") {
            it.get().value("visibility") in ["public", "none"];
        } else if ($variable == "none") {
            it.get().value("visibility") in ["public", "none"];
        } else {
            false;
        }
    } else { 
        false; 
    }
}
GREMLIN
            );
        } elseif ($by === self::VISIBLE_BELOW) {
            // The incoming variable is located below the current one
            // This is contravariant : it only accepts lesser visibilities
            $return = new Command(<<<GREMLIN
filter{ 
    if (it.get().properties("visibility").any()) { 
        if ($variable == "private") {
            it.get().value("visibility") in ["private"];
        } else if ($variable == "protected") {
            it.get().value("visibility") in ["private", "protected"];
        } else if ($variable == "public") {
            it.get().value("visibility") in ["private", "protected", "public", "none"];
        } else if ($variable == "none") {
            it.get().value("visibility") in ["private", "protected", "public", "none"];
        } else {
            false;
        }
    } else { 
        false; 
    }
}
GREMLIN
            );
        }

        return $return;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;
use Exakat\Data\Dictionary;

class CodeIs extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 1 :
                $code = func_get_arg(0);
                $translate = Analyzer::TRANSLATE;
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            case 2:
                $code = func_get_arg(0);
                $translate = func_get_arg(1);
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            default:
            case 3:
                list($code, $translate, $caseSensitive) = func_get_args();
        }

        if (is_array($code) && empty($code)) {
            return new Command(Query::STOP_QUERY);
        }

        $col = $caseSensitive === Analyzer::CASE_INSENSITIVE ? 'lccode' : 'code';

        if ($translate === Analyzer::TRANSLATE) {
            $translatedCode = array();
            $code = makeArray($code);
            $translatedCode = $this->dictCode->translate($code, $caseSensitive === Analyzer::CASE_INSENSITIVE ? Dictionary::CASE_INSENSITIVE : Dictionary::CASE_SENSITIVE);

            if (empty($translatedCode)) {
                return new Command(Query::STOP_QUERY);
            }

            return new Command("has(\"$col\", within(***))", array($translatedCode));
        } else {
            return new Command("has(\"$col\", within(***))", array(makeArray($code)));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToInterface extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run('Interface');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotUppercase extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($property) = func_get_args();
        } else {
            $property = 'fullcode';
        }

        assert($this->assertProperty($property));
        if ($property === 'code') {
            return new Command('filter{it.get().value("code") != it.get().value("lccode").toUpperCase()}');
        } else {
            return new Command('filter{it.get().value("' . $property . '") != it.get().value("' . $property . '").toUpperCase()}');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectTraits extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($variable) = func_get_args();
        } else {
            $variable = 'classes';
        }

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $command = new Command('where( 
__.sideEffect{ ' . $variable . ' = []; }
  .out("USE")
  .out("USE")
  .sideEffect{ ' . $variable . '.add(it.get().value("fullnspath")) ; }
  .fold() 
)
');
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasInstruction extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($atoms) = func_get_args();
        } else {
            $atoms = 'Namespace';
        }

        $this->assertAtom($atoms);
        $diff = $this->normalizeAtoms($atoms);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $linksDown = self::$linksDown;

        return new Command(<<<GREMLIN
where( 
__.repeat( __.in({$linksDown}) ).until(hasLabel("File")).emit( ).hasLabel(within(***))
    )
GREMLIN
, array($diff) );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToLoop extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(Analyzer::LOOPS_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToNamespace extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(array('Namespace', 'Php'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class ReturnCount extends DSL {
    public function run(): Command {
        return new Command('count()');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Optional extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($filter) = func_get_args();

        if (!$filter instanceof Command) {
            assert(false, 'Not requires a Command object, it received a ' . gettype($filter));
        }

        $filter->gremlin = "optional($filter->gremlin)";
        return $filter;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class SamePropertyAsArray extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($property, $name) = func_get_args();
            $caseSensitive = Analyzer::CASE_SENSITIVE;
        } else {
            list($property, $name, $caseSensitive) = func_get_args();
        }

        $this->assertProperty($property);
        $this->assertVariable($name);

        if ($property === 'label') {
            return new Command('filter{ it.get().label() in ' . $name . '}');
        } elseif ($property === 'id') {
            return new Command('filter{ it.get().id() in ' . $name . '}');
        } elseif ($property === 'self') {
            return new Command('filter{ it.get() in ' . $name . '}');
        } elseif ($property === 'code' || $property === 'lccode') {
            if ($caseSensitive === Analyzer::CASE_SENSITIVE) {
                return new Command('filter{ it.get().value("code") in ' . $name . '}');
            } else {
                return new Command('filter{ it.get().value("lccode") in ' . $name . '}');
            }
        } elseif (in_array($property, self::INTEGER_PROPERTY, \STRICT_COMPARISON)) {
            return new Command('filter{ it.get().value("' . $property . '") in ' . $name . '}');
        } else {
            $caseSensitive = $caseSensitive === Analyzer::CASE_SENSITIVE ? '' : '.toLowerCase()';

            return new Command('filter{ it.get().value("' . $property . '")' . $caseSensitive . ' in ' . $name . $caseSensitive . '}');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsUppercase extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($property) = func_get_args();
        } else {
            $property = 'fullcode';
        }

        return new Command('filter{it.get().value("' . $property . '") == it.get().value("' . $property . '").toUpperCase()}');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class NextCalls extends DSL {
    public function run(): Command {

        if(func_num_args() === 1) {
            $times = abs((int) func_get_arg(0));
        } else {
            $times = 1;
        }

        // Starting from Parameter, going to next parameter
        // Need a number of executions?

        if ($times === 0) {
            return new Command(Query::NO_QUERY);
        } else {
            return new Command(<<<GREMLIN
emit().repeat(
     __
     .out("NAME")
     .out("DEFINITION")
     .has("rank")
    // .as("ranked")
     .sideEffect{ ranked = it.get().value('rank');}
     .in("ARGUMENT")
     .in("DEFINITION")
     .out("ARGUMENT")
     .filter{ it.get().value('rank') == ranked;}
    // .where("rank", is(eq("ranked")).by("rank"))
).times($times)
GREMLIN
);
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class InterfaceDefinition extends DSL {
    public function run(): Command {
        return new Command('in("DEFINITION")');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class AtomIsNot extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($atoms, $flags) = func_get_args();
                break;

            case 1:
                list($atoms) = func_get_args();
                $flags = Analyzer::WITHOUT_CONSTANTS;
                break;

            default:
                assert(func_num_args() >= 2, 'Too many arguments for ' . __METHOD__);
                assert(func_num_args() === 0, 'No arguments for ' . __METHOD__);
        }

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        }

        if ($flags === Analyzer::WITH_CONSTANTS) {
            // Ternary are unsupported
            // arrays, members, static members are not supported
            $gremlin = <<<'GREMLIN'
coalesce( __.hasLabel(within(["Identifier", "Nsname", "Staticconstant"])).in("DEFINITION").out("VALUE"),
            // Local constant
          __.hasLabel(within(["Variable"])).in("DEFINITION").out("DEFAULT"),

          // local variable
          __.hasLabel(within(["Variable"])).in("DEFINITION").hasLabel('Variabledefinition', 'Staticdefinition').out("DEFAULT"),
          
          // literal value, passed as an argument (Method, closure, function)
          __.hasLabel(within(["Variable"])).in("DEFINITION").in("NAME").hasLabel('Parameter').sideEffect{ rank = it.get().value('rank');}.in("ARGUMENT").out("DEFINITION").optional(__.out("METHOD")).out("ARGUMENT").filter{ rank == it.get().value('rank');},

          // literal value, passed as an argument
          __.hasLabel(within(["Ternary"])).out("THEN", "ELSE").not(hasLabel('Void')),

          __.hasLabel(within(["Coalesce"])).out("LEFT", "RIGHT"),
          
          // default case, will be filtered by hasLabel()
          __.filter{true})
.not(hasLabel(within(***)))
GREMLIN;
            return new Command($gremlin, array($diff));
        } else {
            // WITHOUT_CONSTANTS or non-constant atoms
            $list = makeList($diff);
            return new Command('not(hasLabel(' . $list . '))');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class ToResults extends DSL {
    public function run(): Command {
        $linksDown = self::$linksDown;
        return new Command(<<<GREMLIN
sideEffect{ line = it.get().value("line");
             fullcode = it.get().value("fullcode");
             file = "None"; 
             theFunction = ""; 
             theClass = ""; 
             theNamespace = ""; 
             }
.where( __.until( hasLabel("Project") ).repeat( 
    __.in({$linksDown})
      .sideEffect{ if (theFunction == "" && it.get().label() in ["Function", "Closure", "Arrowfunction", "Magicmethod", "Method"]) { theFunction = it.get().value("fullcode")} }
      .sideEffect{ if (theClass == ""    && it.get().label() in ["Class", "Trait", "Interface", "Classanonymous"]                ) { theClass = it.get().value("fullcode")   } }
      .sideEffect{ if (it.get().label() == "File") { file = it.get().value("fullcode")} }
       ).fold()
)
.map{ ["fullcode":fullcode, 
       "file":file, 
       "line":line, 
       "namespace":theNamespace, 
       "class":theClass, 
       "function":theFunction,
       "analyzer":"xxxx"];}
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInsideExpression extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $MAX_LOOPING  = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
emit().repeat(
            __.coalesce( __.hasLabel(within(***)).out(),
                         __.hasLabel("Parenthesis").out("CODE"),
                         __.hasLabel("Assignation").out("RIGHT")
                       )
              )
      .times($MAX_LOOPING)
      .hasLabel(within(***))
GREMLIN;
        return new Command($gremlin, array($diff, $diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsMore extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 2:
                list($value1, $value2) = func_get_args();

                $g1 = $this->makeGremlin($value1);
                $g2 = $this->makeGremlin($value2);

                return new Command("filter{ {$g1} > {$g2};}");

            case 1:
                list($value1) = func_get_args();

                $g1 = $this->makeGremlin($value1);

                return new Command("is(gt($g1))");

                break;

            default:
                assert(false, 'Wrong number of argument for ' . __METHOD__ . '. 2 or 1 are expected, ' . func_num_args() . ' provided');
        }
    }

    private function makeGremlin($value): string {
        // It is an integer
        if (is_int($value)) {
            return (string) $value;
        }

        // It is a gremlin variable
        if ($this->isVariable($value)) {
            assert($this->assertVariable($value));
            return $value . '.toLong()';
        }

        // It is a gremlin property
        if ($this->isProperty($value)) {
            assert($this->assertProperty($value));
            return "it.get().value(\"{$value}\").toLong()";
        }

        assert(false, '$value must be int or gremlin variable or property in ' . __METHOD__);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoComparison extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('HasNoInstruction');

        return $return->run('Comparison');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class FullcodeIs extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 2, 'Too many arguments for ' . __METHOD__);

        switch (func_num_args()) {
            case 2:
                list($code, $caseSensitive) = func_get_args();
                break;

            case 1:
                list($code) = func_get_args();
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            default:
                assert(false, 'No enought arguments for ' . __METHOD__);
        }

        $return = new Command('has("fullcode")');
        $propertyIs = $this->dslfactory->factory('propertyIs');
        $code = makeArray($code);

        return $return->add($propertyIs->run('fullcode', $code, $caseSensitive));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Extending extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $MAX_LOOPING = self::$MAX_LOOPING;
        return new Command(<<<GREMLIN
where( __.emit().repeat( __.out("EXTENDS").in("DEFINITION")).times($MAX_LOOPING)
                .out("EXTENDS").has("fullnspath", within(***)) ) 
GREMLIN
, array($fullnspath));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsEqual extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 2:
                list($value1, $value2) = func_get_args();

                $g1 = $this->makeGremlin($value1);
                $g2 = $this->makeGremlin($value2);

                return new Command("filter{ {$g1} == {$g2};}");

            case 1:
                list($value1) = func_get_args();

                $g1 = $this->makeGremlin($value1);

                return new Command("is(eq($g1))");

            default:
                assert(false, 'Wrong number of argument for ' . __METHOD__ . '. 2 or 1 are expected, ' . func_num_args() . ' provided');
        }
    }

    private function makeGremlin($value): string {
        // It is an integer
        if (is_int($value)) {
            return (string) $value;
        }

        // It is a gremlin variable
        if ($this->isVariable($value)) {
            assert($this->assertVariable($value));
            return $value;
        }

        // It is a gremlin property
        if ($this->isProperty($value)) {
            assert($this->assertProperty($value));
            return " it.get().value(\"{$value}\").toLong()";
        }

        assert(false, $value . ' must be an integer, atom property or gremlin variable in ' . __METHOD__);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class FunctioncallIsNot extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $this->assertArguments(1, func_num_args(), __METHOD__);
        assert($fullnspath !== null, 'fullnspath can\'t be null in ' . __METHOD__);

        $diff = $this->normalizeFunctioncalls($fullnspath);

        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        }

        $atomIs = $this->dslfactory->factory('atomIs');
        $return = $atomIs->run('Functioncall', Analyzer::WITHOUT_CONSTANTS);

        $has = $this->dslfactory->factory('has');
        $return->add($has->run('fullnspath'));

        $fullnspathIs = $this->dslfactory->factory('fullnspathIsNot');
        $return->add($fullnspathIs->run(array_values($diff)));

        return $return;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class FullcodeInside extends DSL {
    public function run(): Command {
        list($fullcode) = func_get_args();

        // $fullcode is a name of a variable
        $gremlin = 'emit( ).repeat( out(' . self::$linksDown . ') ).times(' . self::$MAX_LOOPING . ').filter{ it.get().value("fullcode") == ' . $fullcode . '}';
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class AnalyzerIs extends DSL {
    public function run(): Command {
        list($analyzer) = func_get_args();

        $analyzer = makeArray($analyzer);

        if (($id = array_search('self', $analyzer)) !== false) {
            $analyzer[$id] = $this->analyzerQuoted;
        }
        $analyzer = array_map(Analyzer::class . '::getName', $analyzer);

        assert($this->assertAnalyzer($analyzer));

        return new Command('where( __.in("ANALYZED").has("analyzer", within(***)))', array($analyzer));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoDefinition extends DSL {
    public function run(): Command {
        return new Command(<<<'GREMLIN'
not( 
    where(                  // Not a use expression
        __.out("DEFINITION").not( where(__.coalesce( __.in("NAME").in("USE"), __.in("USE"), __.in("USE")).hasLabel("Usenamespace") ) ) 
                            // Not a recursive expression
                            .where( 
                                __.repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File", "Function")).where(neq("first"))
                            )
                            // Not a recursive level 2 expression
                            /*
                            .where(
                                __.repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File", "Function")).where(neq("first")).as('second')
                                  .out("DEFINITION").repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File", "Function"))
                            )
                            */
    )
)
GREMLIN
);
    }
}
/*

*/

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToClassInterfaceTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(Analyzer::CIT);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;
use Exakat\Exceptions\WrongNumberOfArguments;

class NotSamePropertyAs extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($property, $name) = func_get_args();
                $caseSensitive = Analyzer::CASE_SENSITIVE;
                break;

            case 3:
                list($property, $name, $caseSensitive) = func_get_args();
                break;

            default:
                throw new WrongNumberOfArguments(__METHOD__, func_num_args(), 2);
        }

        if ($property !== SavePropertyAs::ATOM) {
            $this->assertProperty($property);
        }
        assert($this->assertVariable($name));

        switch ($property) {
            case 'label':
                return new Command("filter{ it.get().label() != $name }");

            case 'id':
                return new Command("filter{ it.get().id() != $name }");

            case 'self':
                assert(false, 'Dont use self with property');

            case SavePropertyAs::ATOM :
                return new Command("filter{ it.get() != $name }");

            case 'reference':
                return new Command('filter{ if (it.get().properties("reference").any()) { ' . $name . ' != it.get().value("reference");} else { ' . $name . ' != false; }}');

            default :
                if (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
                    return new Command('filter{ if ( it.get().properties("' . $property . '").any()) { ' . $name . ' != it.get().value("' . $property . '")} else {' . $name . ' != false; }; }');
                } elseif (in_array($property, self::INTEGER_PROPERTY, \STRICT_COMPARISON)) {
                    return new Command("has(\"$property\").filter{ it.get().value(\"$property\") != $name}");
                } else {
                    if ($caseSensitive === Analyzer::CASE_SENSITIVE) {
                        $caseSensitive = '';
                    } else {
                        $caseSensitive = '.toLowerCase()';
                    }

                    return new Command("has(\"$property\").filter{ it.get().value(\"$property\")$caseSensitive != $name$caseSensitive}");
                }
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Is extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($property, $value) = func_get_args();

        $this->assertProperty($property);

        if ($value === null) {
            return new Command('has("' . $property . '", null)');
        } elseif ($property === 'rank') {
            if ($value === 'last') {
                return new Command('filter{ it.get().vertices(IN, "ARGUMENT").next().value("count") == it.get().value("rank") + 1}');
            } elseif ($value === '2last') {
                return new Command('filter{ it.get().vertices(IN, "ARGUMENT").next().value("count") == it.get().value("rank") + 2}');
            } else {
                return new Command('has("rank", ' . (int) $value . ')');
            }
        } elseif (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
            $value = $value === true ? 'true' : 'false';

            return new Command('filter{ if ( it.get().properties("' . $property . '").any()) { ' . $value . ' == it.get().value("' . $property . '")} else {' . $value . ' == false; }; }');
        } elseif ($value === true) {
            return new Command('has("' . $property . '", true)');
        } elseif ($value === false) {
            return new Command('has("' . $property . '", false)');
        } elseif (is_int($value)) {
            return new Command('has("' . $property . '", ' . $value . ')');
        } elseif (is_string($value)) {
            return new Command('has("' . $property . '", ***)', array($value));
        } elseif (is_array($value)) {
            return new Command('has("' . $property . '", within(***))', array($value));
        } else {
            assert(false, 'Not understood type for is : ' . gettype($value));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasTryCatch extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run('Try');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class OutWithoutLastRank extends DSL {
    public function run(): Command {
        return new Command('sideEffect{dernier = it.get().value("count") - 1;}.out("EXPRESSION").filter{ it.get().value("rank") < dernier}');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToMethod extends DSL {
    public function run(): Command {
        list($name) = func_get_args();

        $name = mb_strtolower($name);

        // also handle variables
        //assert($this->assertProperty($name));

        $gremlin = <<<GREMLIN
 out("METHOD", "MAGICMETHOD").hasLabel("Method", "Magicmethod")
.out("NAME").filter{ it.get().value("fullcode").toLowerCase() == "$name"}
.in("NAME")
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class ProcessLevels extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($maxLevel) = func_get_args();
            $filter = ".filter{ levels.max() > $maxLevel}";
        } else {
            $filter = '.map{levels;}';
        }

        $MAX_LOOPING = self::$MAX_LOOPING;

        // round() is used for lone blocks in the code
        // it may be excessive
        $command = new Command(<<<GREMLIN
where(
    __.sideEffect{ levels = []; }
      .repeat( __.out("BLOCK", "EXPRESSION", "THEN", "ELSE", "CASES")).emit().times($MAX_LOOPING)
      .not(hasLabel("Sequence", "Block"))
      .path()
      .sideEffect{ levels.add(Math.round((it.get().size() - 1 ) / 2 - 1));}
      .fold()
)$filter
GREMLIN
);

        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class OtherSiblings extends DSL {
    private static $sibling = 0;

    public function run(): Command {
        switch (func_num_args()) {
            case 2:
                list($link, $self) = func_get_args();
                break;

            case 1:
                list($link) = func_get_args();
                $self = Analyzer::EXCLUDE_SELF;
                break;

            default:
                $link = 'EXPRESSION';
                $self = Analyzer::EXCLUDE_SELF;
                break;
        }

        ++self::$sibling;

        if ($self === Analyzer::EXCLUDE_SELF) {
            return new Command('as("sibling' . self::$sibling . '").in("' . $link . '").out("' . $link . '").where(neq("sibling' . self::$sibling . '"))');
        } else {
            return new Command('in("' . $link . '").out("' . $link . '")');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Property extends DSL {
    public function run(): Command {
        list($property, $value) = func_get_args();

        assert($this->assertProperty($property));

        // special case for boolean
        if (is_bool($value)) {
            return new Command('sideEffect{ it.get().property("' . $property . '", ' . ($value === true ? 'true' : 'false') . '); }', array());
        } elseif (is_int($value)) {
            return new Command('sideEffect{ it.get().property("' . $property . '", ' . $value . '); }', array());
        } elseif ($this->isVariable($value)) {
            return new Command('sideEffect{ it.get().property("' . $property . '", ' . $value . '); }', array());
        } elseif (is_string($value)) {
            // Default, a string
            return new Command('sideEffect{ it.get().property("' . $property . '", "' . str_replace('$', '\\$', $value) . '"); }', array());
        } else {
            die('No processing for type ' . gettype($value) . ' in Property DSL.');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class TokenIsNot extends DSL {
    public function run(): Command {
        list($token) = func_get_args();

        assert($this->assertTokens($token));
        return new Command('not( has("token", within(***)) )', array(makeArray($token)) );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsArgument extends DSL {
    public function run(): Command {
        return new Command('where( __.in("DEFINITION").where( __.in("NAME")))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Count extends DSL {
    public function run(): Command {
        return new Command('count()');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasAtomInside extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;
        $diff = $this->normalizeAtoms($atoms);

        $gremlin = "where( __.emit( ).repeat( out($linksDown) ).times($MAX_LOOPING).hasLabel(within(***)) )";

        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsNotClassCompatible extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 is expected, ' . func_num_args() . ' provided');
        list($fnp, $typehint) = func_get_args();

        // ONE or OR :all are missing
        // AND : one missing is enough

        $MAX_LOOPING = self::$MAX_LOOPING;
        $gremlin = <<<GREMLIN
 sideEffect{ t = $typehint;
             fnp = $fnp;
             if (fnp instanceof String) {
                fnp = [fnp];
             }
  }
    // collect all types available
.where( 
    __.sideEffect{ x = []; }
      .union(
                __.filter{ true },
                __.out("EXTENDS", "IMPLEMENTS").emit().repeat(__.in("DEFINITION").out("EXTENDS", "IMPLEMENTS")).times($MAX_LOOPING)
      )
      .has("fullnspath")
      .sideEffect{ x.add(it.get().value("fullnspath")) ; }
      .fold() 
)
.filter{
    if (t == "one") {
        x.intersect(fnp).size() == 0;
    } else if (t == "or") {
        x.intersect(fnp).size() == 0;
    } else if (t == "and") {
        fnp.findAll{ fnp ->
            fnp in x
        }.size() != fnp.size();
    } else {
        // We should never go here
        die; 
        false;
    }
}

GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class DuplicateNode extends DSL {
    public function run(): Command {
        return new Command(<<<'GREMLIN'
 as("source")
.addV(select("source").label()).as("clone").sideEffect(                                       
    // copy vertex properties
    select("source").properties().as("p").
    select("clone").
      property(select("p").key(), select("p").value()))

GREMLIN);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotExtendingComposer extends DSL {
    public function run(): Command {
        $MAX_LOOPING = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
not( 
    where( __.out("EXTENDS")
             .repeat( __.coalesce(__.in("DEFINITION"), __.filter{true}).out("EXTENDS") ).emit().times($MAX_LOOPING)
             .where( __.in("ANALYZED").has("analyzer", "Composer/IsComposerNsname") )
          ) 
)
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToAllParentsTraits extends DSL {
    public const MAX_LOOPING = 6;

    public function run(): Command {
        if (func_num_args() === 1) {
            list($self) = func_get_args();
        } else {
            $self = Analyzer::EXCLUDE_SELF;
        }

        $MAX_LOOPING = self::MAX_LOOPING;
        if ($self === Analyzer::EXCLUDE_SELF) {
            $command = new Command(<<<GREMLIN
identity().as("gotoallparentstraits").repeat( 
    __.union( __.out("USE").out("USE"), __.out("EXTENDS"))
      .in("DEFINITION")
      .hasLabel("Class", "Classanonymous", "Trait")
      .simplePath().from("gotoallparentstraits")
)
.emit( )
.times($MAX_LOOPING)
.dedup()
.hasLabel("Class", "Classanonymous", "Trait")
GREMLIN
);
        } else {
            $command = new Command(<<<GREMLIN
union(
__.identity().as("gotoallparentstraits").repeat( 
    __.union( __.out("USE").out("USE"), __.out("EXTENDS"))
      .in("DEFINITION")
      .hasLabel("Class", "Classanonymous", "Trait")
      .simplePath().from("gotoallparentstraits")
)
.emit( )
.times($MAX_LOOPING)
.hasLabel("Class", "Classanonymous", "Trait"),
identity()
)

GREMLIN
);
        }

        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NextSibling extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = 'EXPRESSION';
        }

        $hasIn = $this->dslfactory->factory('hasIn');
        $return = $hasIn->run($link); // Extra command

        $nextSibling = new Command('sideEffect{sibling = it.get().value("rank");}.in("' . $link . '").out("' . $link . '").filter{sibling + 1 == it.get().value("rank")}');
        $return->add($nextSibling);

        return $return;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsMissingOrNull extends DSL {
    public function run(): Command {
        // check for DEFINTION link and the Virtualproperty atom
        return new Command(<<<'GREMLIN'
 where( __.out("DEFAULT").hasLabel("Void", "Null").not(__.in("RIGHT") ))
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInsideNoAnonymous extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $gremlin = 'emit( ).repeat( __.out(' . self::$linksDown . ').not(hasLabel("Closure", "Classanonymous")) ).times(' . self::$MAX_LOOPING . ').hasLabel(within(***))';
        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToClassInterface extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(array('Interface', 'Class', 'Classanonymous'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CheckTypeWithAtom extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($var) = func_get_args();

        assert($this->assertVariable($var, self::VARIABLE_READ));

        $gremlin = <<<GREMLIN
filter{
    if (!($var instanceof ArrayList)) {
        $var = [$var];
    }
    
    response = true;
    
    for(type in $var)
    if (type == "\\\\int") {
        response = response &
        !((it.get().label() in ["Integer", "Addition", "Multiplication", "Bitshift", "Power"]) ||
           (it.get().label() == "Cast" &&  it.get().value("token") == "T_INT_CAST"));

    } else if (type == "\\\\string") {
        response = response && 
        !((it.get().label() in ["String", "Heredoc", "Concatenation"]) ||
           (it.get().label() == "Cast" &&  it.get().value("token") == "T_STRING_CAST"));

    } else if (type == "\\\\array") {
        response = response && 
        !((it.get().label() in ["Arrayliteral"]) ||
           (it.get().label() == "Cast" &&  it.get().value("token") == "T_ARRAY_CAST"));

    } else if (type == "\\\\float") {
        response = response && 
        !((it.get().label() in ["Float", "Integer"]) ||
           (it.get().label() == "Cast" &&  it.get().value("token") in ["T_DOUBLE_CAST", "T_INT_CAST"]));

    } else if (type == "\\\\bool") {
        response = response && 
        !((it.get().label() in ["Boolean", "Comparison"]) ||
           (it.get().label() == "Cast" &&  it.get().value("token") == "T_BOOL_CAST"));

    } else if (type == "\\\\object") {
        response = response && 
        !((it.get().label() in ["Variable", "New", "Clone"]) ||
           (it.get().label() == "Cast" &&  it.get().value("token") == "T_OBJECT_CAST"));

    } else if (type == "\\\\void") {
        response = response && 
        !(it.get().label() in ["Void"]);

    } else if (type == "\\\\callable") {
        response = response && 
        !(it.get().label() in ["Closure", "Arrowfunction"]);

    } else if (type == "\\\\iterable") {
        if (it.get().label() in ["Arrayliteral"]) {
            response = response && false;
        } else if("fullnspath" in it.get().properties() && it.get().value("fullnspath") in ["\\\\arrayobject", "\\\\iterator"]) {
            response = response && false;
        } else {
            response = response && true;
        }
    } else {
        response = response && true;
    }
    
    response;
}
GREMLIN;
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoAtomInside extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);

        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

        $gremlin = <<<GREMLIN
not(
    where( __.repeat( __.out($linksDown).not(hasLabel("Closure", "Arrowfunction", "Class", "Function", "Classanonymous")) ).emit( )
                     .times($MAX_LOOPING)
                     .hasLabel(within(***)) 
          )
)
GREMLIN;
        return new Command($gremlin, array($diff) );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class IsMoreHash extends DSL {
    public function run(): Command {
        assert(func_num_args() === 3, 'Wrong number of argument for ' . __METHOD__ . '. 3 are expected, ' . func_num_args() . ' provided');
        list($property, $hash, $index) = func_get_args();

        if (empty($hash)) {
            return new Command(Query::STOP_QUERY);
        }

        assert($this->assertProperty($property));

        return new Command("has(\"$property\").filter{ x = ***[$index]; x != null; }.filter{ it.get().value(\"$property\") > x}", array($hash));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Filter extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($filter) = func_get_args();

        if (!$filter instanceof Command) {
            assert(false, 'Not requires a Command object, it received a ' . gettype($filter));
        }

        $filter->gremlin = "where( {$filter->gremlin} )";
        return $filter;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToAllTraits extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($self) = func_get_args();
        } else {
            $self = Analyzer::INCLUDE_SELF;
        }

        $MAX_LOOPING = self::$MAX_LOOPING;

        if ($self === Analyzer::EXCLUDE_SELF) {
            $command = new Command(<<<GREMLIN
 as("gotoalltraits")
.repeat( __.out("USE")
          .hasLabel("Usetrait")
          .out("USE")
          .in("DEFINITION")
          //.hasLabel("Trait")
          .simplePath().from("gotoalltraits")
      ).emit( )
      .times($MAX_LOOPING)
      .hasLabel("Trait")
GREMLIN
);
        } elseif ($self === Analyzer::INCLUDE_SELF) {
            $command = new Command(<<<GREMLIN
 as("gotoalltraits")
.emit( )
.repeat( __.out("USE")
          .hasLabel("Usetrait")
          .out("USE")
          .in("DEFINITION")
          .hasLabel("Trait")
          .simplePath().from("gotoalltraits")
        )
        .times($MAX_LOOPING)
        .hasLabel("Trait")
GREMLIN
);
        } else {
            assert(false, 'No such configuration for ' . self::class . ' : use EXCLUDE_SELF or INCLUDE_SELF');
        }

        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsAllOf extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($properties, $value) = func_get_args();

        assert(is_array($properties), 'Property argument should be an array. ' . gettype($properties) . " was provided\n");
        foreach($properties as $property) {
            $this->assertProperty($property);
        }

        $command = array();
        if ($value === null) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", null)';
            }
/*        } elseif (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
            $value = $value === true ? 'true' : 'false';

            return new Command('filter{ if ( it.get().properties("' . $property . '").any()) { ' . $value . ' == it.get().value("' . $property . '")} else {' . $value . ' == false; }; }');
        */} elseif ($value === true) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", true)';
            }
        } elseif ($value === false) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", false)';
            }
        } elseif (is_int($value)) {
            foreach($properties as $property) {
                $command[] = 'has("' . $property . '", ' . $value . ')';
            }
        } elseif (is_string($value)) {
            assert(false, 'Not done yet');
            return new Command('has("' . $property . '", ***)', array($value));
        } elseif (is_array($value)) {
            assert(false, 'Not done yet');
            return new Command('has("' . $property . '", within(***))', array($value));
        } else {
            assert(false, 'Not understood type for is : ' . gettype($value));
        }

        return new Command('and(' . join(', ', $command) . ')');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class NoDelimiterIsNot extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 2, 'Too many arguments for ' . __METHOD__);

        switch (func_num_args()) {
            case 2:
                list($code, $caseSensitive) = func_get_args();
                break;

            case 1:
                list($code) = func_get_args();
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            default:
                assert(false, 'No enought arguments for ' . __METHOD__);
        }

        $return = new Command('has("noDelimiter")');
        $propertyIsNot = $this->dslfactory->factory('propertyIsNot');

        return $return->add($propertyIsNot->run('noDelimiter', $code, $caseSensitive));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsReassigned extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($name) = func_get_args();

        $linksDown = self::$linksDown;
        $MAX_LOOPING = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
not(
    where( 
        __.repeat( __.out($linksDown) ).emit(hasLabel("Variable", "Array", "Member", "Staticproperty")).times($MAX_LOOPING)
                     .filter{ it.get().value("fullcode") == $name}
         )
)
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoTraitDefinition extends DSL {
    public function run(): Command {
        return new Command('not( where(__.in("DEFINITION").hasLabel("Trait") ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToAllParents extends DSL {
    public const MAX_LOOPING = 6;

    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($self) = func_get_args();

        $MAX_LOOPING = self::MAX_LOOPING;
        if ($self === Analyzer::EXCLUDE_SELF) {
            $command = new Command(<<<'GREMLIN'
as("gotoallparents").repeat( __.out("EXTENDS", "IMPLEMENTS")
          .in("DEFINITION")
          .hasLabel("Class", "Classanonymous", "Interface", "Trait")
          .simplePath().from("gotoallparents")
).emit( )
 .times(5)
 .hasLabel("Class", "Classanonymous", "Interface", "Trait")
GREMLIN
);

        } else {
            $command = new Command(<<<'GREMLIN'
as("gotoallparents").emit( )
.repeat( __.out("EXTENDS", "IMPLEMENTS")
           .in("DEFINITION")
           .hasLabel("Class", "Classanonymous", "Interface", "Trait")
           .simplePath().from("gotoallparents")
        )
        .times(5)
        .hasLabel("Class", "Classanonymous", "Interface", "Trait")
GREMLIN
);
        }

        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GetStringLength extends DSL {
    public function run(): Command {
        // Calculate The length of a string in a property, and report it in the named string
        switch (func_num_args()) {
            case 2:
                list($property, $variable) = func_get_args();
                break;

            case 1:
                list($property) = func_get_args();
                $variable = 1;
                break;

            case 0:
                $property = 'noDelimiter';
                $variable = 1;
                break;

            default:
                assert(false, 'No enought arguments for ' . __METHOD__);
        }

        $gremlin = <<<'GREMLIN'
sideEffect{
    s = it.get().value("PROPERTY");
    
    // Replace all special chars by a single char
    s = s.replaceAll(/\\"/, "A");
    s = s.replaceAll(/\\[\\aefnRrt]/, "A");
    s = s.replaceAll(/\\[012]\d\d/, "A");
    s = s.replaceAll(/\\0/, "A");
    s = s.replaceAll(/\\u\{[^\}]+\}/, "A");
    s = s.replaceAll(/\\[pP]\{^?[A-Z][a-z]?\}/, "A");
    s = s.replaceAll(/\\[pP][A-Z]/, "A");
    s = s.replaceAll(/\\X[A-Z][a-z]/, "A");
    s = s.replaceAll(/\\x[a-fA-F0-9]{2}/, "A");

    VARIABLE = s.length();
}

GREMLIN;

        $gremlin = str_replace(array('PROPERTY', 'VARIABLE'), array($property, $variable), $gremlin);

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToAllElse extends DSL {
    public function run(): Command {
        $MAX_LOOPING = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
repeat( __.out("ELSE")).emit().times($MAX_LOOPING).hasLabel("Ifthen")
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsThis extends DSL {
    public function run(): Command {
        return new Command(<<<'GREMLIN'
or( __.hasLabel("This"),

    // Typehinted variable
    __.hasLabel("Variableobject", "Variable").in("DEFINITION").in("NAME").as("definition").out("TYPEHINT").in("DEFINITION").as("typehint").select("definition").in("ARGUMENT").in("METHOD", "MAGICMETHOD").as("classe").where("typehint", eq("classe") ),

    // Typehinted property
    __.hasLabel("Member").in("DEFINITION").in("PPP").as("definition").out("TYPEHINT").in("DEFINITION").as("typehint").select("definition").in("PPP").as("classe").where("typehint", eq("classe") )
  )
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GetNameInFNP extends DSL {
    public function run(): Command {
        list($variable) = func_get_args();

        return new Command(<<<GREMLIN
sideEffect{
    if ($variable.contains("\\\\") ) {
        $variable = $variable.tokenize("\\\\\\\\").last(); 
    }
    if ($variable.contains("(") ) {
        $variable = $variable.tokenize("(").first(); 
    }
}
GREMLIN
                          );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Command {
    public const SACK_NONE    = '';
    public const SACK_ARRAY   = '{ [] }{it.clone()}';
    public const SACK_HASH    = '{ [:] }{it.clone()}';
    public const SACK_INTEGER = '{ 0 }';

    private static $id = 0;
    public $gremlin    = '';
    public $arguments  = array();
    private $sack      = self::SACK_NONE;

    public function __construct(string $command, array $args = array()) {
        $c = substr_count($command, '***');

        assert($c === count($args), "Wrong number of arguments for Command : $c placeholders, " . count($args) . " provided. ($command)\n" . print_r(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), true));

        $arguments = array();
        foreach($args as $arg) {
            ++self::$id;
            $arguments['arg' . self::$id] = $arg;
        }

        $command = str_replace(array('%', '***'), array('%%', '%s'), $command);
        $command = sprintf($command, ...array_keys($arguments));

        $this->gremlin = $command;
        $this->arguments = $arguments;
    }

    public function setSack(string $default = self::SACK_NONE): void {
        assert(in_array($default, array(self::SACK_NONE,
                                        self::SACK_ARRAY,
                                        self::SACK_HASH,
                                        self::SACK_INTEGER,
                                        ), \STRICT_COMPARISON),
              'Sack must be one of the allowed constant : "' . $default . '" provided');

        $this->sack = $default;
    }

    public function getSack(): string {
        return $this->sack;
    }

    public function add(self $other): self {
        $this->gremlin   .= ".{$other->gremlin}";
        $this->arguments += $other->arguments;

        return $this;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoFunctionInside extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        // $fullcode is a name of a variable
        $gremlin = 'not( where( __.emit( ).repeat( out(' . self::$linksDown . ') ).times(' . self::$MAX_LOOPING . ').hasLabel("Functioncall").has("fullnspath", within(***))) )';
        return new Command($gremlin, array(makeArray($fullnspath)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectMethods extends DSL {
    public const METHOD_ALL = 1;
    public const METHOD_ABSTRACT = 2;
    public const METHOD_CONCRETE = 3;

    public function run(): Command {
        switch(func_num_args()) {
            case 1 :
                $variable = func_get_arg(0);
                $methods = self::METHOD_ALL;
                break;

            case 2:
                $variable = func_get_arg(0);
                $methods = func_get_arg(1);
                break;

            default:
                assert(false, 'collectMethods needs 1 or 2 arguments : ' . func_num_args() . ' were provided.');
        }

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        switch($methods) {
            default:
            case self::METHOD_ALL:
                $final = "{$variable} = {$variable}.keySet();";
                break;

            case self::METHOD_ABSTRACT:
                $final = "{$variable} = {$variable}.findAll{ k,v -> v == true}.keySet();";
                break;

            case self::METHOD_CONCRETE:
                $final = "{$variable} = {$variable}.findAll{ k,v -> v == false}.keySet();";
                break;
        }

        // methods are collected from current class till above.
        // No trait involved
        // only the first method found is used (others, with same name are ignored)
        // abstract option is included
        $command = new Command(<<<GREMLIN
 sideEffect{ {$variable} = [:]; }
.where(
    __
    .where( 
        __.out("METHOD", "MAGICMETHOD")
          .sideEffect{ if({$variable}[it.get().value("lccode")] == null) { {$variable}[it.get().value("lccode")] = it.get().properties("abstract").any(); } }
          .out("NAME")
          .fold() 
        )
    .where(
        __
        .repeat(out("EXTENDS").in("DEFINITION")).emit()
        .where( 
            __
              .out("METHOD", "MAGICMETHOD")
              .sideEffect{ if({$variable}[it.get().value("lccode")] == null) { {$variable}[it.get().value("lccode")] = it.get().properties("abstract").any(); } }
              .out("NAME")
              .fold() 
            )
            .fold()
    )
)
.sideEffect{ $final }
GREMLIN
);
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Exceptions\QueryException;

class IsNot extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($property, $value) = func_get_args();

        $this->assertProperty($property);

        if ($value === null) {
            return new Command("has(\"$property\").or( __.not(has(\"$property\")), __.not(has(\"$property\", null)))");
        } elseif (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
            $value = $value === true ? 'true' : 'false';

            return new Command('filter{ if ( it.get().properties("' . $property . '").any()) { ' . $value . ' != it.get().value("' . $property . '")} else {' . $value . ' != false; }; }');
        } elseif ($value === true) {
            return new Command("has(\"$property\", false)");
        } elseif ($value === false) {
            return new Command("has(\"$property\", true)");
        } elseif (is_int($value)) {
            return new Command("has(\"$property\").not(has(\"$property\", ***))", array($value));
        } elseif (is_string($value)) {
            if (empty($value)) {
                return new Command("has(\"$property\").not(has(\"$property\", ''))");
            } else {
                return new Command("has(\"$property\").not(has(\"$property\", ***))", array($value));
            }
        } elseif (is_array($value)) {
            if (empty($value)) {
                return new Command("has(\"$property\").not(has(\"$property\", ''))");
            } else {
                return new Command("has(\"$property\").not(has(\"$property\", within(***)))", array($value));
            }
        } else {
            throw new QueryException('Not understood type for isNot : ' . gettype($value));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GetVariable extends DSL {
    public function run(): Command {
        // getVariable($variable => $name of the variable)
        if (func_num_args() === 1) {
            list($variable) = func_get_args();
            $name = $variable;
        } else {
            list($variable, $name) = func_get_args();
        }

        if (is_string($variable) && is_string($name)) {
            // Value should not be a direct groovy code!!!
            $this->assertVariable($variable, self::VARIABLE_READ);
            return new Command('map{ [' . $name . ':' . $variable . ']; }');
        } elseif (is_array($variable) && is_array($name)) {
            $name = array_values($name);
            $gremlin = array();

            foreach(array_values($variable) as $id => $v) {
                // Value should not be a direct groovy code!!!
                $this->assertVariable($v, self::VARIABLE_READ);
                $gremlin[] = "\"{$name[$id]}\" :  $v";
            }
            return new Command('map{ [' . implode(',' . PHP_EOL, $gremlin) . '] }');
        }

        assert(false, 'Wrong format for ' . __METHOD__ . '. Either string/value or array()/array()');
    }
}
?>
<?php
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/
declare(strict_types = 1);

namespace Exakat\Query\DSL;

use Exakat\Exceptions\UnknownDsl;
use Exakat\GraphElements;
use Exakat\Analyzer\Analyzer;

class DSLFactory {
    public const VARIABLE_WRITE = true;
    public const VARIABLE_READ  = false;

    public $availableAtoms         = array();
    public $availableLinks         = array();
    public $availableFunctioncalls = array();
    private $availableVariables     = array(); // This one is per query
    protected $availableLabels        = array(); // This one is per query
    protected $ignoredcit             = array();
    protected $ignoredfunctions       = array();
    protected $ignoredconstants       = array();
    protected $dictCode               = null;
    protected $datastore              = null;
    protected $linksDown              = '';
    protected $dependsOn              = array();
    protected $analyzerQuoted         = '';
    protected $MAX_LOOPING            = Analyzer::MAX_LOOPING;

    public function __construct(string $analyzer,
                                array $dependsOn = array()) {
        $this->dependsOn = $dependsOn;
        $this->analyzerQuoted = $analyzer;


        $this->dictCode  = exakat('dictionary');
        $this->datastore = exakat('datastore');

        $this->linksDown = GraphElements::linksAsList();

        if (empty($this->availableAtoms)) {
            $data = $this->datastore->getCol('TokenCounts', 'token');

            $this->availableAtoms = array('Project',
                                          'File',
                                          'Virtualproperty',
                                          'Analysis',
                                          'Noresult',
                                          'Void',
                                          'Identifier',
                                          );
            $this->availableLinks = array('DEFINITION',
                                          'ANALYZED',
                                          'PROJECT',
                                          'FILE',
                                          'OVERWRITE',
                                          'PPP',
                                          'DEFAULT',
                                          'RETURNED',
                                          'TYPEHINT',
                                          );

            foreach($data as $token){
                if ($token === strtoupper($token)) {
                    $this->availableLinks[] = $token;
                } else {
                    $this->availableAtoms[] = $token;
                }
            }

            $this->availableFunctioncalls = $this->datastore->getCol('functioncalls', 'functioncall');

            $this->ignoredcit       = $this->datastore->getCol('ignoredcit',       'fullnspath');
            $this->ignoredfunctions = $this->datastore->getCol('ignoredfunctions', 'fullnspath');
            $this->ignoredconstants = $this->datastore->getCol('ignoredconstants', 'fullnspath');
        }
    }

    public function factory(string $name): Dsl {
        if (strtolower($name) === '_as') {
            $className = __NAMESPACE__ . '\\_As';
        } else {
            $className = __NAMESPACE__ . '\\' . ucfirst($name);
        }

        if (!class_exists($className)) {
            throw new UnknownDsl($name);
        }

        $return = new $className($this,
                              $this->availableAtoms,
                              $this->availableLinks,
                              $this->availableFunctioncalls,
                              $this->availableVariables,
                              $this->availableLabels,
                              $this->ignoredcit,
                              $this->ignoredfunctions,
                              $this->ignoredconstants,
                              $this->dependsOn,
                              $this->analyzerQuoted
                              );

        $last = explode('\\', $return::class);
        $last = array_pop($last);
        assert($className == $return::class, "Wrongly Cased DSL : $name instead of " . $last);

        return $return;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoChildWithRank extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($links, $rank) = func_get_args();
        } else {
            list($links) = func_get_args();
            $rank = 0;
        }

        $this->assertLink($links);

        if (is_int($rank)) {
            return new Command('not( where( __.out(' . $this->SorA($links) . ').has("rank", ***) ) )', array(abs($rank)));
        } elseif ($this->isVariable($rank)) {
            assert($this->assertVariable($rank), "$rank is not a variable");

            return new Command('not( where( __.out(' . $this->SorA($links) . ').filter{it.get().value("rank") == ' . $rank . '; } ) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Implementing extends DSL {
    public function run(): Command {
        list($fullnspath) = func_get_args();

        $MAX_LOOPING = self::$MAX_LOOPING;
        return new Command(<<<GREMLIN
where( __.emit().repeat( __.out("IMPLEMENTS", "EXTENDS").in("DEFINITION")).times({$MAX_LOOPING})
                        .out("IMPLEMENTS", "EXTENDS").has("fullnspath", within(***)) ) 
GREMLIN
, array($fullnspath));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class MakeVariableName extends DSL {
    public function run(): Command {
        list($variable) = func_get_args();

        return new Command("sideEffect{ $variable = \"\\$\" + $variable; }");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInsideMoreThan extends DSL {
    public function run(): Command {
        if (func_get_args() === 2) {
            list($atoms, $times) = func_get_args();
        } else {
            $atoms = func_get_arg(0);
            $times = 1;
        }

        $this->assertAtom($atoms);
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $linksDown = self::$linksDown;
        $MAX_LOOPING  = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
where(
    __.emit( ).repeat( __.out({$linksDown}).not(hasLabel("Closure", "Classanonymous", "Function", "Class", "Trait", "Interface")) ).times($MAX_LOOPING)
      .hasLabel(within(***))
      .count().is(gt($times))
)
GREMLIN;
        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run('Trait');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToExpression extends DSL {
    public function run(): Command {
        $linksDown = self::$linksDown;

        return new Command(<<<GREMLIN
coalesce( __.where( __.in("EXPRESSION")), 
          __.repeat( __.in({$linksDown})).emit( ).until( where(__.in("EXPRESSION") ) ).where( __.in("EXPRESSION") )
        )
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasClass extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run(Analyzer::CLASSES_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoCatch extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run('Catch');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInside extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        $this->assertAtom($atoms);
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $gremlin = 'emit().repeat( out(' . self::$linksDown . ') ).times(' . self::$MAX_LOOPING . ').hasLabel(within(***))';
        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasNoIn extends DSL {
    public function run(): Command {
        list($links) = func_get_args();

        assert($this->assertLink($links));

        $diff = $this->normalizeLinks($links);
        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        } else {
            return new Command('not( where( __.in(' . $this->SorA($diff) . ') ) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class SaveMethodNameAs extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($name) = func_get_args();

        $this->assertVariable($name, self::VARIABLE_WRITE);

        $gremlin = <<<GREMLIN
sideEffect{ 
    x = it.get().value("fullnspath").tokenize("::"); 
    $name = x[1]; 
}

GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class HasClassInterface extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run(array('Class', 'Classanonymous', 'Interface'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Select extends DSL {
    public function run(): Command {
        list($values) = func_get_args();

        $by     = array();
        $select = array();
        foreach($values as $k => $v) {
            assert(in_array($k, $this->availableLabels, \STRICT_COMPARISON), "No such step as '$k'");

            if (is_int($k)) {
                $select[] = "by(constant($v))";
            } elseif ($v === 'id') {
                $select[] = $k;
                $by[]     = 'by(id())';
            } elseif (in_array($v, self::PROPERTIES, \STRICT_COMPARISON)) {
                // Use a local property
                $select[] = $k;
                $by[]     = "by(\"$v\")";
            } elseif (substr(trim($v), 0, 2) === '__') {
                // __.out('BLOCK').count()
                $select[] = $k;
                $by[]     = "by($v)";
            } else {
                // Turn value into a constant
                $select[] = $k;
                $by[]     = "by(constant(\"$v\"))";
            }
        }

        if (empty($by)) {
            $command = 'select(' . makeList($select) . ')';
        } else {
            $command = 'select(' . makeList($select) . ').' . implode('.', $by);
        }

        return new Command($command);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasAttribute extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($attributes) = func_get_args();

        $goToClass = $this->dslfactory->factory('goToClass');
        $return = $goToClass->run();

        $goToAllParents = $this->dslfactory->factory('goToAllParents');
        $return->add($goToAllParents->run(Analyzer::INCLUDE_SELF));

        $outIs = $this->dslfactory->factory('outIs');
        $return->add($outIs->run('ATTRIBUTE'));

        $is = $this->dslfactory->factory('is');
        $return->add($is->run('fullnspath', makeArray($attributes)));

        return $return;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasNoChildren extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($childrenClass, $outs) = func_get_args();

        $this->assertAtom($childrenClass);
        $diff = $this->normalizeAtoms($childrenClass);
        if (empty($diff)){
            return new Command(Query::NO_QUERY);
        }

        $out = $this->makeLinks($outs, 'out');

        return new Command("not( where( __$out.hasLabel(within(***)) ) )", array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class InitVariable extends DSL {
    public const TYPE_CODE = 2;
    public const TYPE_ARGUMENT = 3;

    public function run(): Command {
        if (func_num_args() === 3) {
            list($name, $value, $type) = func_get_args();
            if (!in_array($type, array(self::TYPE_CODE, self::TYPE_ARGUMENT))) {
                $type = self::TYPE_ARGUMENT;
            }
        } elseif (func_num_args() === 2) {
            list($name, $value) = func_get_args();
            $type = self::TYPE_CODE;
        } else {
            list($name) = func_get_args();
            $value = '[]';
            $type = self::TYPE_CODE;
        }

        $this->assertVariable($name, self::VARIABLE_WRITE);

        // Here, there is one name for the variable
        if ($type === self::TYPE_ARGUMENT) {
            return new Command('sideEffect{ ' . $name . ' = *** }', array($value));
        }

        // Here, there is one name for the variable
        if ($type === self::TYPE_CODE) {
            return new Command('sideEffect{ ' . $name . ' = ' . $value . ' }');
        }

        assert(false, 'Wrong format for ' . __METHOD__ . '. Either string/value or array()/array()');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class SetProperty extends DSL {
    public function run(): Command {
        list($property, $value) = func_get_args();

        $this->assertProperty($property);
        if ($value === true) {
            return new Command("sideEffect{ it.get().property(\"$property\", true); }");
        } elseif ($value === false) {
            return new Command("sideEffect{ it.get().property(\"$property\", false); }");
        } else {
            return new Command("sideEffect{ it.get().property(\"$property\", $value); }");
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInsideWithCall extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $linksDown = self::$linksDown;
        $MAX_LOOPING  = self::$MAX_LOOPING;

        $list = makeList($diff);

        $gremlin = <<<GREMLIN
emit( ).repeat( __.out($linksDown)
                  .coalesce( __.hasLabel("Functioncall").in("DEFINITION").out("BLOCK").out("EXPRESSION"), 
                             __.filter{true})
                  .not(hasLabel("Closure", "Classanonymous", "Function", "Class", "Trait", "Interface")) 
            )
            .times($MAX_LOOPING)
            .hasLabel($list)
GREMLIN;
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class IsCalled extends DSL {
    public function run(): Command {
        return new Command('where( __.out("DEFINITION").hasLabel(' . makeList(Analyzer::FUNCTIONS_CALLS) . '))');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class Range extends DSL {
    public function run(): Command {
        list($min, $max) = func_get_args();

        return new Command('range(' . (int) $min . ', ' . (int) $max . ')');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class ToCobbled extends DSL {
    public function run(): Command {
        return new Command('property("cobbled", true)');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class NextSiblings extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = 'EXPRESSION';
        }

        $hasIn = $this->dslfactory->factory('hasIn');
        $return = $hasIn->run($link);

        return $return->add(new Command('sideEffect{sibling = it.get().value("rank");}.in("' . $link . '").out("' . $link . '").filter{sibling + 1 <= it.get().value("rank") }'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasIfthen extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run('Ifthen');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class AnalyzerInside extends DSL {
    public function run(): Command {
        list($analyzers) = func_get_args();

        $gremlin = 'emit().repeat( out(' . self::$linksDown . ') ).times(' . self::$MAX_LOOPING . ').where( __.in("ANALYZED").has("analyzer", within(***)))';
        return new Command($gremlin, array($analyzers));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class GoToClassTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(Analyzer::CLASSES_TRAITS);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectArguments extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 2:
                list($variable, $property) = func_get_args();
                $this->assertProperty($property);
                break;

            case 1:
                list($variable) = func_get_args();
                $property = 'code';
                break;

            default:
                assert(false, 'wrong number of argument for ' . __METHOD__);
        }

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $command = new Command(<<<GREMLIN
where( 
__.sideEffect{ $variable = []; }
  .out("ARGUMENT")
  .order().by("rank")
  .optional(__.out("NAME"))
  .sideEffect{ $variable.add(it.get().value("$property")) ; }
  .fold() 
)
GREMLIN
);
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class VariableIsAssigned extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($times) = func_get_args();

        $gremlin = <<<GREMLIN
where(
   __.out("DEFINITION").in("LEFT")
     .hasLabel("Assignation").has("token", "T_EQUAL")
     .not(where(__.in("EXPRESSION").in("INIT")))
     .out("RIGHT").hasLabel("Integer", "String", "Float", "Null", "Boolean")
     .count().is(gte($times))
)
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsReferencedArgument extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 1:
                list($variable) = func_get_args();
                break;

            case 0:
                $variable = 'variable';
                break;

            default:
                assert(false, 'wrong number of argument for ' . __METHOD__);
        }

        $gremlin = <<<GREMLIN
not(
    where(
        __.repeat( __.in()).until(hasLabel("Function")).out("ARGUMENT").filter{it.get().value("code") == $variable}.has("reference", true)
    )
)

GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class HasNoParent extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($parentClass, $ins) = func_get_args();

        if (empty($ins)){
            return new Command(Query::NO_QUERY);
        }

        $this->assertAtom($parentClass);
        $this->assertLink($ins);
        $diff = $this->normalizeAtoms($parentClass);

        if (empty($diff)){
            return new Command(Query::NO_QUERY);
        }

        $in = $this->makeLinks($ins, 'in');

        return new Command("not( where( __$in.hasLabel(within(***)) ) )", array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AnalyzerInsideMoreThan extends DSL {
    public function run(): Command {
        list($analyzer, $atoms, $times) = func_get_args();

        assert($this->assertAtom($atoms));
        assert($this->assertAnalyzer($analyzer));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $linksDown = self::$linksDown;
        $MAX_LOOPING  = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
where(
    __.emit( ).repeat( __.out({$linksDown}).not(hasLabel("Closure", "Classanonymous", "Function", "Class", "Trait", "Interface")) ).times($MAX_LOOPING)
      .hasLabel(within(***))
      .where( __.in("ANALYZED").has("analyzer", within(***)))
      .count().is(gt($times))
)
GREMLIN;
        return new Command($gremlin, array($diff, makeArray($analyzer)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class _As extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($name) = func_get_args();

        assert($this->assertLabel($name, self::LABEL_SET));

        if (is_array($name)) {
            return new Command('as("' . implode('").as("', $name) . '")');
        } else {
            return new Command("as(\"$name\")");
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class SameTypehintAs extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($variable) = func_get_args();

        $this->assertVariable($variable);

        return new Command('where( __.out("RETURNTYPE", "TYPEHINT").has("fullnspath").filter{it.get().value("fullnspath") == ' . $variable . '} )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class OutToParameter extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($rank) = func_get_args();

//out("ARGUMENT").sideEffect{ it.get().value("rank") == $rank || (it.get().value("rank") < $rank && ("variadic" in it.get().keys()) && it.get().value("variadic") == true);}

        $query = <<<GREMLIN
out("ARGUMENT").filter{ it.get().value("rank") == $rank || (it.get().value("rank") < $rank && ("variadic" in it.get().keys()) && it.get().value("variadic") == true);}
        
GREMLIN;

        return new Command($query, array());
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class HasNoNextSibling extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($link) = func_get_args();
        } else {
            $link = 'EXPRESSION';
        }

        $hasIn = $this->dslfactory->factory('hasIn');
        $return = $hasIn->run($link);

        return $return->add(new Command('not( where( __.sideEffect{sibling = it.get().value("rank");}.in("' . $link . '").out("' . $link . '").filter{sibling + 1 == it.get().value("rank")}) )'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class PropertyIs extends DSL {
    public function run(): Command {
        list($property, $code, $caseSensitive) = func_get_args();

        assert($this->assertProperty($property));

        if (is_array($code) && empty($code) ) {
            return new Command(Query::NO_QUERY);
        }

        if ($caseSensitive === Analyzer::CASE_SENSITIVE) {
            $caseSensitive = '';
        } elseif ($caseSensitive === Analyzer::CASE_INSENSITIVE) {
            $code = $this->tolowercase($code);
            $caseSensitive = '.toString().toLowerCase()';
        } else {
            assert(false, 'No such case sensitivity : "' . $caseSensitive . '"');
        }

        // code is a variable. We don't know if it is an array
        if (is_array($code) && !empty(array_intersect($code, $this->availableVariables))) {
            return new Command('filter{it.get().value("' . $property . '")' . $caseSensitive . ' == ' . $code[0] . '}', array());
        } elseif (is_string($code) && in_array($code, $this->availableVariables)) {
            return new Command('filter{it.get().value("' . $property . '")' . $caseSensitive . ' == ' . $code . '}', array());
        } elseif (is_array($code)) {
            return new Command('filter{ it.get().value("' . $property . '")' . $caseSensitive . ' in ***; }', array($code));
        } else {
            return new Command('filter{it.get().value("' . $property . '")' . $caseSensitive . ' == ***}', array($code));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class AtomInsideNoDefinition extends DSL {
    public function run(): Command {
        list($atoms) = func_get_args();

        assert($this->assertAtom($atoms));
        $diff = $this->normalizeAtoms($atoms);
        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $linksDown = self::$linksDown;
        $MAX_LOOPING  = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
emit( ).repeat( __.out($linksDown)
                  .not(hasLabel("Closure", "Classanonymous", "Function", "Class", "Trait", "Interface")) 
            )
            .times($MAX_LOOPING)
            .hasLabel(within(***))
GREMLIN;
        return new Command($gremlin, array($diff));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class Not extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($filter) = func_get_args();

        if (!$filter instanceof Command) {
            assert(false, 'Not() requires a Command object, it received a ' . gettype($filter));
        }

        if ($filter->gremlin === Query::STOP_QUERY) {
            $filter->gremlin = Query::NO_QUERY;
        } else {
            $filter->gremlin = "not( __.where($filter->gremlin))";
        }
        return $filter;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class NoQuery extends DSL {
    public function run(): Command {
        return new Command(Query::NO_QUERY);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class GoToAllRight extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 1:
                list($atom) = func_get_args();
                break;

            default:
                $atom = 'Logical';
        }

        $MAX_LOOPING = self::$MAX_LOOPING;

        $gremlin = <<<GREMLIN
union(
    __.out('LEFT'),
    __.emit().repeat( __.hasLabel("$atom").out("RIGHT")).times($MAX_LOOPING).out('LEFT', 'RIGHT')
).not(hasLabel("$atom"))
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Exceptions\WrongNumberOfArguments;

class AddETo extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($edgeName, $from) = func_get_args();
            $options = array();
        } elseif (func_num_args() === 3) {
            list($edgeName, $from, $options) = func_get_args();
            assert(is_array($options), 'Edge options must be an array');
        } else {
            throw new WrongNumberOfArguments(__METHOD__, func_num_args(), 3);
        }

        assert($this->assertLabel($from, self::LABEL_GO));

        $properties = array();
        foreach ($options as $name => $value) {
            $properties[] = ".property(\"$name\", $value)";
        }
        $properties = implode('', $properties);

        return new Command("addE(\"$edgeName\").to(\"$from\")$properties");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class ToDropEdge extends DSL {
    public function run(): Command {
        list($link) = func_get_args();

        $this->assertLink($link);

        return new Command('outE("' . $link . '").property("toDrop", true)');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasNoClass extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');
        return $return->run(Analyzer::CLASSES_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class CollectContainers extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($variable) = func_get_args();
        } else {
            $variable = 'containers';
        }

        $CONTAINERS = makeList(Analyzer::CONTAINERS);
        $LINKS_DOWN = self::$linksDown;

        return new Command(<<<GREMLIN
where(
    __.sideEffect{ $variable = []; }
      .repeat( __.out($LINKS_DOWN)).emit()
      .hasLabel($CONTAINERS)
      .sideEffect{ 
          $variable.add(it.get().value("code")); 
      }
      .fold()
)

GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class FullcodeIsNot extends DSL {
    public function run(): Command {
        assert(func_num_args() <= 2, 'Too many arguments for ' . __METHOD__);

        switch (func_num_args()) {
            case 2:
                list($code, $caseSensitive) = func_get_args();
                break;

            case 1:
                list($code) = func_get_args();
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            default:
                assert(false, 'Not enough arguments for ' . __METHOD__);
        }

        $return = new Command('has("fullcode")');
        $propertyIsNot = $this->dslfactory->factory('propertyIsNot');

        return $return->add($propertyIsNot->run('fullcode', $code, $caseSensitive));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class StopQuery extends DSL {
    public function run(): Command {
        return new Command(Query::STOP_QUERY);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class SavePropertyAs extends DSL {
    public const ATOM = 'whole';

    public function run(): Command {
        assert(func_num_args() <= 2, __METHOD__ . ' should get 2 arguments max, ' . func_num_args() . ' provided.');

        if (func_num_args() === 1) {
            $property = self::ATOM;
            list($name) = func_get_args();
        } else {
            list($property, $name) = func_get_args();
            if ($property !== self::ATOM) {
                $this->assertProperty($property);
            }
        }

        $this->assertVariable($name, self::VARIABLE_WRITE);

        if ($property === self::ATOM) {
            return new Command('sideEffect{ ' . $name . ' = it.get(); }');
        } elseif ($property === 'label') {
            return new Command('sideEffect{ ' . $name . ' = it.get().label(); }');
        } elseif ($property === 'id') {
            return new Command('sideEffect{ ' . $name . ' = it.get().id(); }');
        } elseif ($property === 'self') {
            assert(false, 'Dont use self anymore for properties');
            return new Command('sideEffect{ ' . $name . ' = it.get(); }');
        } elseif (in_array($property, array('reference'), \STRICT_COMPARISON) ) {
            return new Command('sideEffect{ if (it.get().properties("' . $property . '").any()) { ' . $name . ' = it.get().value("' . $property . '");} else { ' . $name . ' = false; }}');
        } elseif (in_array($property, self::BOOLEAN_PROPERTY, \STRICT_COMPARISON)) {
            return new Command('sideEffect{ if ( it.get().properties("' . $property . '").any()) { ' . $name . ' = it.get().value("' . $property . '")} else {' . $name . ' = false; }; }');
        } else {
            return new Command('has("' . $property . '").sideEffect{ ' . $name . ' = it.get().value("' . $property . '"); }');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;
use Exakat\Data\Dictionary;

class CodeIsNot extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 1 :
                $code = func_get_arg(0);
                $translate = Analyzer::TRANSLATE;
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            case 2:
                $code = func_get_arg(0);
                $translate = func_get_arg(1);
                $caseSensitive = Analyzer::CASE_INSENSITIVE;
                break;

            default:
            case 3:
                list($code,$translate, $caseSensitive) = func_get_args();
        }

        if (is_array($code) && empty($code)) {
            return new Command(Query::NO_QUERY);
        }

        $col = $caseSensitive === Analyzer::CASE_INSENSITIVE ? 'lccode' : 'code';

        if ($translate === Analyzer::TRANSLATE) {
            $translatedCode = array();
            $code = makeArray($code);
            $translatedCode = $this->dictCode->translate($code, $caseSensitive === Analyzer::CASE_INSENSITIVE ? Dictionary::CASE_INSENSITIVE : Dictionary::CASE_SENSITIVE);

            if (empty($translatedCode)) {
                // Couldn't find anything in the dictionary : OK!
                return new Command(Query::NO_QUERY);
            }

            return new Command("not(has(\"$col\", within(***)))", array($translatedCode));
        } else {
            return new Command("not(has(\"$col\", within(***)))", array(makeArray($code)));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToTypehint extends DSL {
    public function run(): Command {
        // todo limit to one type of SOURCE

        // constants, variables, static member, variableoboject, etc.

        $gremlin = <<<'GREMLIN'
coalesce(
    // (new x)
    __.hasLabel("Parenthesis").out("CODE").hasLabel("New").out("NEW").out("TYPEHINT"),
    __.hasLabel("Parenthesis").out("CODE").hasLabel("Clone").out("CLONE").out("TYPEHINT"),

    __.hasLabel("Variable", "Variableobject", "Variablearray").in("DEFINITION").in("NAME").hasLabel("Parameter").out("TYPEHINT"),
    // depends on Complete/Variabletypehint
    __.hasLabel("Variable", "Variableobject", "Variablearray").in("DEFINITION").hasLabel("Variabledefinition").out("TYPEHINT"),

    __.hasLabel("Member").in("DEFINITION").hasLabel("Propertydefinition").in("PPP").hasLabel("Ppp").out("TYPEHINT"),
    __.hasLabel("Staticproperty").in("DEFINITION").hasLabel("Propertydefinition").in("PPP").hasLabel("Ppp").out("TYPEHINT"),

    __.hasLabel("Staticconstant").in("DEFINITION").hasLabel("Constant").out("TYPEHINT"),
    __.hasLabel("Identifier", "Nsname").in("DEFINITION").hasLabel("Constant").out("TYPEHINT"),

    __.hasLabel("Functioncall", "Methodcall", "Staticmethodcall").in("DEFINITION").hasLabel("Function", "Method", "Magicmethod", "Arrowfunction", "Closure").out("RETURNTYPE")
    
)
GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class Back extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($name) = func_get_args();

        assert($this->assertLabel($name, self::LABEL_GO));
        return new Command("select(\"$name\")");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class FollowCalls extends DSL {
    public function run(): Command {

        $TIME_LIMIT = self::$TIME_LIMIT;

        switch(func_num_args()) {
            case 1:
                $loopings = (int) func_get_arg(0);
                break;

           default:
                $loopings = self::$MAX_LOOPING;
                break;
        }

        // Coalesce is not supported
        return new Command(<<<GREMLIN
emit().repeat(
    __.timeLimit($TIME_LIMIT).out("NAME").out("DEFINITION")
      .union(__.identity(),
            // local assignation to variable
            __.emit().repeat(
                 __.in("DEFAULT").hasLabel("Variabledefinition").out("DEFINITION")
             ).times(4)
      )
      .as("a").in("ARGUMENT").in("DEFINITION").out("ARGUMENT").as("b")
      .where("a", eq("b") ).by("rank")
).times($loopings)
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CountBy extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 3:
                list($link, $property, $variable) = func_get_args();
                break;

            case 2:
                list($link, $property) = func_get_args();
                $variable = 'v';
                break;

            case 1:
                list($link) = func_get_args();
                $variable = 'v';
                $property = 'fullcode';
                break;

            case 0:
            default:
                $variable = 'v';
                $property = 'fullcode';
                $link = 'EXPRESSION';
                break;
        }

        $this->assertLink($link);
        $this->assertProperty($property);
        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $gremlin = <<<GREMLIN
where(  
    __.sideEffect{ {$variable} = [:]; }
      .out("$link")
      .sideEffect{
        s = it.get().value("$property"); 
        if ({$variable}[s] != null) { 
            {$variable}[s]++; 
        } else { 
            {$variable}[s] = 1;
        } 
      }.fold()
     )
GREMLIN;
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToCurrentScope extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('goToInstruction');

        return $return->run(array('Function', 'Phpcode'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class NoCodeInside extends DSL {
    public function run(): Command {
        list($atom, $values) = func_get_args();

        $atomFilter = makeList(makeArray($atom));

        assert($this->assertAtom($atom));
        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

        // $fullcode is a name of a variable
        $gremlin = <<<GREMLIN
not(
    __.where( 
        __.repeat( __.out({$linksDown})).emit().times($MAX_LOOPING)
          .hasLabel($atomFilter)
          .filter{ it.get().value("code") in $values; }
    )
)

GREMLIN;
        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasNoClassTrait extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');

        return $return->run(array('Class', 'Classanonymous', 'Trait', 'Method', 'Magicmethod'));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class RegexIsNot extends DSL {
    public function run(): Command {
        assert(func_num_args() === 2, 'Wrong number of argument for ' . __METHOD__ . '. 2 are expected, ' . func_num_args() . ' provided');
        list($property, $regex) = func_get_args();

        $this->assertProperty($property);

        if ($property === 'code') {
            $values = $this->dictCode->grep($regex);

            if (empty($values)) {
                return new Command(Query::NO_QUERY);
            }

            return new Command('not( has("code", within(***) ) )', array($values));
        }

        return new Command(<<<GREMLIN
has("$property")
.filter{ !(it.get().value("$property") =~ "$regex" ).asBoolean() }
GREMLIN
                          );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectOut extends DSL {
    public function run(): Command {
        if (func_num_args() === 3) {
            list($variable, $out, $property) = func_get_args();
        } elseif (func_num_args() === 2) {
            list($variable, $out) = func_get_args();
            $property = 'fullcode';
        } else {
            assert(false, __METHOD__ . ' requires 2 or 3 arguments, ' . func_num_args() . ' passed.');
        }

        $this->assertVariable($variable, self::VARIABLE_WRITE);
        $this->assertLink($out);
        $this->assertProperty($property);

        return new Command(<<<GREMLIN
where( 
    __.sideEffect{ $variable = []; }
      .out("$out")
      .sideEffect{ $variable.add(it.get().value("$property")) ; }
      .fold() 
) 

GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class DistinctFrom extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($variable) = func_get_args();

        $this->assertLabel($variable);

        return new Command("where(neq('$variable'))");
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class HasInterface extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasInstruction');

        return $return->run('Interface');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsNullable extends DSL {
    public const EXPLICIT = true;
    public const IMPLICIT = false;

    public function run(): Command {
        switch(func_num_args()) {
            case 1:
                list($nullable) = func_get_args();
                $nullable = in_array($nullable, array(self::EXPLICIT, self::IMPLICIT), \STRICT_COMPARISON) ? $nullable : self::IMPLICIT;
                break;

            case 0:
                $nullable = self::IMPLICIT;
                break;

            default:
                assert(func_num_args() == 1, 'Wrong number of argument for ' . __METHOD__ . '. 1 is expected, ' . func_num_args() . ' provided');
        }

        if ($nullable === self::IMPLICIT) {
            return new Command('where( __.out("RETURNTYPE", "TYPEHINT").or(
                __.hasLabel("Scalartypehint").has("fullnspath", "\\\\null"),
                __.hasLabel("Null")
                ))');
        } else {
            return new Command('where( __.out("RETURNTYPE", "TYPEHINT").hasLabel("Scalartypehint").has("fullnspath", "\\\\null") )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class InterfaceLike extends DSL {
    public function run(): Command {
        return new Command('coalesce( __.hasLabel("Class").has("abstract", true),
                                      __.hasLabel("Interface")
                                         )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class FollowValue extends DSL {
    public function run(): Command {

        $TIME_LIMIT = self::$TIME_LIMIT;

        switch(func_num_args()) {
            case 1:
                $loopings = (int) func_get_arg(0);
                break;

           default:
                $loopings = self::$MAX_LOOPING;
                break;
        }

        // Coalesce is not supported
        return new Command(<<<GREMLIN
repeat(
    __.timeLimit($TIME_LIMIT).union(
        // \$b = \$a; => \$b
        __.in("DEFAULT").out("DEFINITION"),
        // foo(\$a) => function (\$c)
        __.as('a').in("ARGUMENT").in("DEFINITION").out("ARGUMENT").as("b").where("a", eq("b") ).by("rank").out("NAME").out("DEFINITION"),
        // foo(bar(\$a)) => function foo(\$c)
        __.in("RETURNED").out("DEFINITION"),
        // global
        __.hasLabel("Variable").in('DEFINITION').as('c').in('DEFINITION').hasLabel('Virtualglobal').out('DEFINITION').out('DEFINITION'),
        // property, static or not
        __.hasLabel("Property", "Staticproperty").in('DEFINITION').hasLabel('Propertydefinition').out('DEFINITION')
    )
).emit().times($loopings)
GREMLIN
);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class NoClassDefinition extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($type) = func_get_args();
        } else {
            $type = Analyzer::CLASSES_ALL;
        }

        $list = makeList($type);

        return new Command('not(where(__.in("DEFINITION").hasLabel(within(' . $list . ')) ) )');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotLowercase extends DSL {
    public function run(): Command {
        if (func_num_args() === 1) {
            list($property) = func_get_args();
        } else {
            $property = 'fullcode';
        }

        assert($this->assertProperty($property));
        if ($property === 'code') {
            return new Command('filter{it.get().value("code") != it.get().value("lccode")}');
        } else {
            return new Command('filter{it.get().value("' . $property . '") != it.get().value("' . $property . '").toLowerCase()}');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class IsClassCompatible extends DSL {
    public function run(): Command {
        if (func_num_args() === 2) {
            list($fnp, $typehint) = func_get_args();
        } elseif (func_num_args() === 1) {
            list($fnp) = func_get_args();
            $typehint = '"one"';
        } else {
            assert(false, 'Wrong number of argument for ' . __METHOD__ . '. 2 is expected, ' . func_num_args() . ' provided');
        }

        // ONE and OR : one is sufficient
        // AND : ALL must be validate

        $MAX_LOOPING = self::$MAX_LOOPING;
        $gremlin = <<<GREMLIN
    // collect all types available
 sideEffect{ t = $typehint;
             fnp = $fnp;
             if (fnp instanceof String) {
                fnp = [fnp];
             }
  }
. where( 
    __.sideEffect{ x = []; }
      .union(
                __.identity(),
                __.out("EXTENDS", "IMPLEMENTS").emit().repeat(__.in("DEFINITION").out("EXTENDS", "IMPLEMENTS")).times($MAX_LOOPING)
      )
      .has("fullnspath")
      .sideEffect{ x.add(it.get().value("fullnspath")) ; }
      .fold() 
)
.filter{
    if (t == "one") {
        x.intersect(fnp).size() != 0;
    } else if (t == "or") {
        x.intersect(fnp).size() != 0;
    } else if (t == "and") {
        fnp.findAll{ fnp ->
            fnp in x
        }.size() == fnp.size();
    } else {
        // We should never go here
        die; 
        false;
    }
}

GREMLIN;

        return new Command($gremlin);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectKeys extends DSL {
    public function run(): Command {
        list($variable, $property) = func_get_args();

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        $command = new Command(<<<GREMLIN
 sideEffect{ {$variable} = []; }
.where( 
    __.out("ARGUMENT")
      .hasLabel('Keyvalue')
      .out('INDEX')
      .sideEffect{ {$variable}.add(it.get().value("$property")) ; }
      .fold() 
    )
GREMLIN
);
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsLess extends DSL {
    public function run(): Command {
        switch(func_num_args()) {
            case 2:
                list($value1, $value2) = func_get_args();

                $g1 = $this->makeGremlin($value1);
                $g2 = $this->makeGremlin($value2);

                return new Command("filter{ {$g1} < {$g2};}");

            case 1:
                list($value1) = func_get_args();
                $g1 = $this->makeGremlin($value1);

                return new Command("is(lt($g1))");

            default:
                assert(false, 'Wrong number of argument for ' . __METHOD__ . '. 2 or 1 are expected, ' . func_num_args() . ' provided');
        }
    }

    private function makeGremlin($value): string {
        // It is an integer
        if (is_int($value)) {
            return (string) $value;
        }

        // It is a gremlin variable
        if ($this->isVariable($value)) {
            assert($this->assertVariable($value));
            return $value . '.toLong()';
        }

        // It is a gremlin property
        if ($this->isProperty($value)) {
            assert($this->assertProperty($value));
            return " it.get().value(\"{$value}\").toLong()";
        }

        assert(false, '$value must be int or gremlin variable or property in ' . __METHOD__);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class CollectTypehints extends DSL {
    public function run(): Command {
        list($variable) = func_get_args();

        $this->assertVariable($variable, self::VARIABLE_WRITE);

        // "TYPEHINT", "RETURNTYPE" : makes it compatible for functions and properties
        $command = new Command(<<<GREMLIN
 where( 
    __.sideEffect{ {$variable} = []; }
      .out("TYPEHINT", "RETURNTYPE")
      .has("fullnspath")
      .sideEffect{ {$variable}.add(it.get().value("fullnspath")) ; }
      .fold() 
    )
GREMLIN
);
        return $command;
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;
use Exakat\Analyzer\Analyzer;

class IsNotHash extends DSL {
    public function run(): Command {
        switch (func_num_args()) {
            case 3 :
                list($property, $hash, $index) = func_get_args();
                $case = Analyzer::CASE_SENSITIVE;
                break;

            case 4 :
                list($property, $hash, $index, $case) = func_get_args();
                assert(in_array($case, array(Analyzer::CASE_INSENSITIVE, Analyzer::CASE_SENSITIVE)));
                break;

            default:
                assert(false, 'Wrong number of arguments for ' . __METHOD__);
        }

        if (empty($hash)) {
            return new Command(Query::NO_QUERY);
        }

        assert($this->assertProperty($property));

        // Cannot make this to work with contains / in (Classes/CouldBeProtectedProperty)
        if ($case === Analyzer::CASE_INSENSITIVE) {
            return new Command("has(\"$property\").filter{ x = ***[$index].collect{ it.toLowerCase() }; [it.get().value(\"$property\").toLowerCase()].intersect(x) == []; }", array($hash));
        } else {
            return new Command("has(\"$property\").filter{ [it.get().value(\"$property\")].intersect(***[$index]) == []}", array($hash));
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;


class IsNotIgnored extends DSL {
    public function run(): Command {
        return new Command('not(has("ignored_dir", true))', array() );
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class InIsNot extends DSL {
    public function run(): Command {
        $this->assertArguments(1, func_num_args(), __METHOD__);
        list($link) = func_get_args();

        $this->assertLink($link);

        $diff = $this->normalizeLinks($link);
        if (empty($diff)) {
            return new Command(Query::NO_QUERY);
        } else {
            return new Command('not( where( __.inE(' . $this->SorA($diff) . ')) )');
        }
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Query\Query;

class NoAnalyzerInsideWithProperty extends DSL {
    public function run(): Command {
        list($atoms, $analyzer, $property, $value) = func_get_args();

        $this->assertProperty($property);
        $this->assertAtom($atoms);
        $this->assertAnalyzer($analyzer);

        $diff = $this->normalizeAtoms($atoms);

        if (empty($diff)) {
            return new Command(Query::STOP_QUERY);
        }

        $MAX_LOOPING = self::$MAX_LOOPING;
        $linksDown = self::$linksDown;

$gremlin = <<<GREMLIN
not( 
    where( __.emit( ).repeat( __.out({$linksDown}) ).times($MAX_LOOPING)
             .hasLabel(within(***))
             .filter{ it.get().value("$property") == $value}
             .where( __.in("ANALYZED").has("analyzer", within(***)))
          )     
)
GREMLIN;
        return new Command($gremlin,
                           array($diff, makeArray($analyzer)));
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

class GoToImplements extends DSL {
    public function run(): Command {
        return new Command('out("IMPLEMENTS").in("DEFINITION")');
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/


namespace Exakat\Query\DSL;

use Exakat\Analyzer\Analyzer;

class HasNoFunction extends DSL {
    public function run(): Command {
        $return = $this->dslfactory->factory('hasNoInstruction');
        return $return->run(Analyzer::FUNCTIONS_ALL);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Analyzer\Rulesets;
use Exakat\Exakat;
use Exakat\Exceptions\MissingGremlin;
use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\NoFileToProcess;
use Exakat\Exceptions\NoSuchReport;
use Exakat\Tasks\Helpers\ReportConfig;
use Exception;
use Exakat\Vcs\Vcs;
use Exakat\Config;

class Onefile extends Tasks {
    public const CONCURENCE = self::NONE;

    protected $rulesetsToRun = array('OneFile',
                                    );

    protected $reports       = array('Text',
                                    );
    protected $reportConfigs = array();

    public function __construct(bool $subTask = self::IS_NOT_SUBTASK) {
        parent::__construct($subTask);

        if (empty($this->reports)) {
            $this->reports = makeArray($this->config->project_reports);
        }
    }

    public function run(): void {
        $project = $this->config->project;

        if (empty($this->config->filename)) {
            display('This command requires at least one file, with the -f option');
            return;
        }

        if (!$project->validate()) {
            throw new InvalidProjectName($project->getError());
        }

        if ($this->config->gremlin === 'NoGremlin') {
            throw new MissingGremlin();
        }

        //Clean datastore
        $this->datastore = exakat('datastore');
        // Reset datastore for the others

        $this->logTime('Start');
        $this->addSnitch(array('step'    => 'Start',
                               'project' => $this->config->project));

        $audit_start = time();
        $this->datastore->addRow('hash', array('audit_start'      => $audit_start,
                                               'exakat_version'   => Exakat::VERSION,
                                               'exakat_build'     => Exakat::BUILD,
                                               'php_version'      => $this->config->phpversion,
                                               'audit_name'       => $this->generateName()
                                         ));

        $info = array();
        $this->datastore->addRow('hash', $info);

        $rulesetsToRun = array($this->rulesetsToRun);
        $namesToRun    = array();

        foreach($this->reports as $format) {
            try {
                $reportConfig = new ReportConfig($format, $this->config);
            } catch (NoSuchReport $e) {
                // Simple ignore
                display($e->getMessage());
                continue;
            }
            $this->reportConfigs[$reportConfig->getName()] = $reportConfig;

            $rulesets = $reportConfig->getRulesets();
            if (empty($rulesets)) {
                $rulesets = $reportConfig->getRulesets();
            }
            $rulesetsToRun[] = $rulesets;
            $namesToRun[] = $reportConfig->getName();

            unset($reportConfig);
            gc_collect_cycles();
        }

        $rulesetsToRun = array_merge(...$rulesetsToRun);
        $rulesetsToRun = array_filter($rulesetsToRun);
        $rulesetsToRun = array_unique($rulesetsToRun);

        $availableRulesets = $this->rulesets->listAllRulesets();
        $availableRulesets = array_map('strtolower', $availableRulesets);

        $diff = array();
        $rulesetsToRunShort = array();
        foreach($rulesetsToRun as $rule) {
            if (in_array(strtolower($rule), $availableRulesets, \STRICT_COMPARISON)) {
                $rulesetsToRunShort[] = $rule;
            } else {
                $diff[] = $rule;
            }
        }

        if (!empty($diff)) {
            display('Ignoring the following unknown rulesets : ' . implode(', ', $diff) . PHP_EOL);
        }

        $rulesetsToRun = array_unique($this->rulesetsToRun);

        display('Running one page');
        display('Running the following analysis : ' . implode(', ', $rulesetsToRun));
        display('Producing the following reports : ' . implode(', ', $namesToRun));

        display('Cleaning DB' . PHP_EOL);
        $args = array ( 1 => 'cleandb',
                        2 => '-p',
                        3 => 'onefile',
                        4 => '-Q',
                        );
        $configThema = new Config($args);

        $analyze = new CleanDb(self::IS_SUBTASK);
        $analyze->setConfig($configThema);
        $analyze->run();
        unset($analyze);
        $this->logTime('CleanDb');
        $this->addSnitch(array('step'    => 'Clean DB',
                               'project' => $this->config->project));
        $this->gremlin->init();

        $this->checkTokenLimit();

        $load = new Load(self::IS_SUBTASK);
        try {
            $load->run();
        } catch (NoFileToProcess $e) {
            $this->datastore->addRow('hash', array('init error' => $e->getMessage(),
                                                   'status'     => 'Error',
                                           ));
        }
        unset($load);
        display("Project loaded\n");
        $this->logTime('Loading');

        // Always run this one first
        $this->analyzeRulesets(array('First'), $audit_start, $this->config->verbose);

        // Dump is a child process
        // initialization and first collection (action done once)
        display('Initial dump');
        $dumpConfig = $this->config->duplicate(array('collect'            => false,
                                                     'load_dump'          => false,
                                                     'project_rulesets'   => array('First')));
        $firstDump = new Dump(self::IS_SUBTASK);
        $firstDump->setConfig($dumpConfig);
        $firstDump->run();
        unset($firstDump);
        $this->logTime('Initial dump');

        if (empty($this->config->program)) {
            $this->analyzeRulesets($rulesetsToRun, $audit_start, $this->config->verbose);
        } else {
            $this->analyzeOne($this->config->program, $audit_start, $this->config->verbose);
        }

        display('Analyzed project' . PHP_EOL);
        $this->logTime('Analyze');
        $this->addSnitch(array('step'    => 'Analyzed',
                               'project' => $this->config->project));

        $this->logTime('Analyze');

        $dump = new Dump(self::IS_SUBTASK);
        foreach($this->config->rulesets as $name => $analyzers) {
            $dump->checkRulesets($name, $analyzers);
        }

        $this->logTime('Reports');
        try {
            $reportConfig = $this->config->duplicate(array('project_reports'    => $this->reports,
                                                           'project_rulesets'   => $this->rulesetsToRun));

            $report = new Report(self::IS_SUBTASK);
            $report->setConfig($reportConfig);

            $report->run();
        } catch (\Throwable $e) {
            display('Error while building the reports : ' . $e->getMessage() . "\n");
        }
        display('Reported project' . PHP_EOL);

        // Reset cache from Rulesets
        Rulesets::resetCache();
        $this->logTime('Final');
        $this->removeSnitch();
        display('End' . PHP_EOL);
    }

    private function logTime(string $step): void {
        static $log, $begin, $end, $start;

        if ($log === null) {
            $log = fopen("{$this->config->log_dir}/project.timing.csv", 'w+');
        }

        $end = microtime(true);
        if ($begin === null) {
            $begin = $end;
            $start = $end;
        }

        fwrite($log, $step . "\t" . ($end - $begin) . "\t" . ($end - $start) . PHP_EOL);
        $begin = $end;
    }

    private function analyzeOne(array $analyzers, int $audit_start, bool $verbose): void {
        $this->addSnitch(array('step'    => 'Analyzer',
                               'project' => $this->config->project));

        try {
            $analyzeConfig = $this->config->duplicate(array('noRefresh' => true,
                                                            'update'    => true,
                                                            'program'   => $analyzers,
                                                            'verbose'   => $verbose,
                                                            'quiet'     => !$verbose,
                                                            ));

            $analyze = new Analyze(self::IS_SUBTASK);
            $analyze->run();
            unset($analyze);
            unset($analyzeConfig);
            $this->logTime('Analyze : ' . makeList($analyzers, ''));

            $dumpConfig = $this->config->duplicate(array('update'    => true,
                                                         'load_dump' => true,
                                                         'program'   => $analyzers,
                                                         ));

            $audit_end = time();
            $query = 'g.V().count()';
            $res = $this->gremlin->query($query);
            $nodes = $res[0];
            $query = 'g.E().count()';
            $res = $this->gremlin->query($query);
            $links = $res[0];

            $this->datastore->addRow('hash', array('audit_end'    => $audit_end,
                                                   'audit_length' => $audit_end - $audit_start,
                                                   'graphNodes'   => $nodes,
                                                   'graphLinks'   => $links));

            $dump = new Dump(self::IS_SUBTASK);
            $dump->run();
            unset($dump);
            unset($dumpConfig);
        } catch (\Exception $e) {
            echo "Error while running the Analyzer {$this->config->project}.\nTrying next analysis.\n";
            file_put_contents("{$this->config->log_dir}/analyze.final.log", $e->getMessage());
        }
    }

    private function analyzeRulesets($rulesets, int $audit_start,bool $verbose): void {
        if (empty($rulesets)) {
            $rulesets = $this->config->project_rulesets;
        }

        if (!is_array($rulesets)) {
            $rulesets = array($rulesets);
        }

        display('Running the following rulesets : ' . implode(', ', $rulesets) . PHP_EOL);

        global $VERBOSE;
        $oldVerbose = $VERBOSE;
        $VERBOSE = false;
        foreach($rulesets as $ruleset) {
            $this->addSnitch(array('step'    => 'Analyze : ' . $ruleset,
                                   'project' => $this->config->project));
            $rulesetForFile = strtolower(str_replace(' ', '_', trim($ruleset, '"')));

            try {
                $analyzeConfig = $this->config->duplicate(array('noRefresh'        => true,
                                                                'update'           => true,
                                                                'project_rulesets' => array($ruleset),
                                                                'program'          => '',
                                                                'verbose'          => $verbose,
                                                                'quiet'            => !$verbose,
                                                                ));

                $analyze = new Analyze(self::IS_SUBTASK);
                $analyze->setConfig($analyzeConfig);
                $analyze->run();
                unset($analyze);
                unset($analyzeConfig);
                $this->logTime("Analyze : $ruleset");

                $audit_end = time();
                $query = 'g.V().count()';
                $res = $this->gremlin->query($query);
                if (isset($res->results)) {
                    $nodes = $res->results[0];
                } else {
                    $nodes = $res[0];
                }
                $query = 'g.E().count()';
                $res = $this->gremlin->query($query);
                if (isset($res->results)) {
                    $links = $res->results[0];
                } else {
                    $links = $res[0];
                }

                $finalMark = array('audit_end'    => $audit_end,
                                   'audit_length' => $audit_end - $audit_start,
                                   'graphNodes'   => $nodes,
                                   'graphLinks'   => $links);
                $this->datastore->addRow('hash', $finalMark);

                // Skip Dump, as it is auto-saving itself.
                $dumpConfig = $this->config->duplicate(array('update'               => true,
                                                             'project_rulesets'     => array($ruleset),
                                                             'load_dump'            => true,
                                                             'verbose'              => false,
                                                             ));

                $dump = new Dump(self::IS_SUBTASK);
                $dump->setConfig($dumpConfig);
                $dump->run();
                $dump->finalMark($finalMark);
                unset($dump);
                unset($dumpConfig);

                gc_collect_cycles();
                $this->logTime("Dumped : $ruleset");
            } catch (Exception $e) {
                display("Error while running the ruleset $ruleset.\nTrying next ruleset.\n");
                file_put_contents("{$this->config->log_dir}/analyze.$rulesetForFile.final.log", $e->getMessage(), FILE_APPEND);
            }
        }
        $VERBOSE = $oldVerbose;
    }

    private function generateName(): string {
        $ini = parse_ini_file("{$this->config->dir_root}/data/audit_names.ini");

        $names = $ini['names'];
        $adjectives = $ini['adjectives'];

        shuffle($names);
        shuffle($adjectives);

        try {
            $x = random_int(0, PHP_INT_MAX);
        } catch (\Throwable $t) {
            $x = (int) microtime(true) * 1000000;
        }
        $name = $names[ $x % (count($names) - 1)];

        try {
            $x = random_int(0, PHP_INT_MAX);
        } catch (\Throwable $t) {
            $x = (int) microtime(true) * 1000000;
        }
        $adjective = $adjectives[ $x % (count($adjectives) - 1)];

        return ucfirst($adjective) . ' ' . $name;
    }

    private function getLineDiff(string $current, VCS $vcs): void {
        if ($this->config->dump_previous === null) {
            return ;
        }

        if (!file_exists($this->config->dump_previous)) {
            return ;
        }

        $sqlite = new \Sqlite3($this->config->dump_previous);
        $res = $sqlite->query('SELECT name FROM sqlite_master WHERE type="table" AND name="hash"');
        if ($res === false || !$res->numColumns() || $res->columnType(0) == SQLITE3_NULL) {
            return;
        }

        $res = $sqlite->query('SELECT value FROM hash WHERE key="vcs_revision"');
        if (!$res->numColumns() || $res->columnType(0) == SQLITE3_NULL) {
            return;
        }
        $revision = $res->fetchArray(\SQLITE3_ASSOC)['value'];

        $diff = $vcs->getDiffLines($revision, $current);
        if (!empty($diff)) {
            $this->datastore->addRow('linediff', $diff);
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Config;
use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\NoCodeInProject;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Vcs\Vcs;

class Update extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    protected $logname = self::LOG_NONE;

    public function run(): void {
        $project = $this->config->project;

        if (!$project->validate()) {
            throw new InvalidProjectName($project->getError());
        }

        if ($this->config->project->isDefault()) {
            $this->runDefault();
        } else {
            $this->runProject();
        }
    }

    private function runDefault(): void {
        if (!file_exists("{$this->config->projects_root}/projects")) {
            display("This installation has no projects directory. Aborting all update. Provide .exakat.ini to enable update in this folder.\n");
            return;
        }

        $paths = glob("{$this->config->projects_root}/projects/*");
        $projects = array_map('basename', $paths);
        $projects = array_diff($projects, array('test'));

        echo 'Updating ' . count($projects) . ' projects' . PHP_EOL;
        shuffle($projects);
        foreach ($projects as $project) {
            display("updating $project\n");

            $args = array(1 => 'update',
                          2 => '-p',
                          3 => $project,
            );
            $updateConfig = new Config($args);

            $this->update($updateConfig);
        }
    }

    private function runProject(): void {
        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($this->config->project);
        }

        if (!is_dir($this->config->project_dir)) {
            throw new NoSuchProject($this->config->project);
        }

        if (!file_exists($this->config->code_dir)) {
            throw new NoCodeInProject($this->config->project);
        }

        // clean all previous sql caches
        $files = glob("{$this->config->project_dir}/.exakat/dump-*.php");
        display('Removing ' . count($files) . " dump-*.php files\n");
        foreach($files as $file) {
            unlink($file);
        }

        $this->update($this->config);
    }

    private function update(Config $updateConfig): void {
        $vcs = Vcs::getVcs($updateConfig);
        $vcs = new $vcs($updateConfig->project, $updateConfig->code_dir);

        display("Code update $updateConfig->project with " . $vcs->getName());
        $new = $vcs->update();
        if ($new === Vcs::NO_UPDATE) {
            display('No update available. Skipping');

            return;
        }

        display($vcs->getName() . " updated to $new");

        display('Running files');
        shell_exec($this->config->php . ' exakat files -p ' . $updateConfig->project);
    }
}
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Helpers\Timer;
use Exakat\GraphElements;
use Exakat\Graph\Graph;
use Exakat\Project;
use Exakat\Exceptions\InvalidPHPBinary;
use Exakat\Exceptions\LoadError;
use Exakat\Exceptions\MustBeAFile;
use Exakat\Exceptions\MustBeADir;
use Exakat\Exceptions\NoFileToProcess;
use Exakat\Exceptions\NoSuchLoader;
use Exakat\Exceptions\UnknownCase;
use Exakat\Tasks\LoadFinal\LoadFinal;
use Exakat\Tasks\Helpers\Fullnspaths;
use Exakat\Tasks\Helpers\AtomInterface;
use Exakat\Tasks\Helpers\AtomGroup;
use Exakat\Tasks\Helpers\Calls;
use Exakat\Tasks\Helpers\Encoding;
use Exakat\Tasks\Helpers\Context;
use Exakat\Tasks\Helpers\Intval;
use Exakat\Tasks\Helpers\Strval;
use Exakat\Tasks\Helpers\Boolval;
use Exakat\Tasks\Helpers\Nullval;
use Exakat\Tasks\Helpers\Constant;
use Exakat\Tasks\Helpers\Precedence;
use Exakat\Tasks\Helpers\IsPhp;
use Exakat\Tasks\Helpers\IsStub;
use Exakat\Tasks\Helpers\IsExt;
use Exakat\Tasks\Helpers\IsRead;
use Exakat\Tasks\Helpers\IsModified;
use Exakat\Tasks\Helpers\Php;
use Exakat\Tasks\Helpers\Sequences;
use Exakat\Tasks\Helpers\NestedCollector;
use Exakat\Tasks\Helpers\ContextVariables;
use Exakat\Tasks\Helpers\ClassTraitContext;
use Exakat\Tasks\Helpers\AnonymousNames;
use ProgressBar\Manager as ProgressBar;
use Exakat\Loader\Collector;
use Exakat\Log\Timing as TimingLog;
use Exakat\Fileset\{All, Filenames, FileExtensions, IgnoreDirs};

class Load extends Tasks {
    public const CONCURENCE = self::NONE;

    // @todo : Move this outside the code, to handle PHP versions
    private $SCALAR_TYPE = array('int',
                                 'bool',
                                 'void',
                                 'float',
                                 'string',
                                 'array',
                                 'callable',
                                 'iterable',
                                 'object',
                                 'false',
                                 'null',
                                 'mixed',
                                 'never',
                                 );

    private $PHP_SUPERGLOBALS = array('$GLOBALS',
                                      '$_SERVER',
                                      '$_GET',
                                      '$_POST',
                                      '$_FILES',
                                      '$_REQUEST',
                                      '$_SESSION',
                                      '$_ENV',
                                      '$_COOKIE',
                                      '$php_errormsg',
                                      '$HTTP_RAW_POST_DATA',
                                      '$http_response_header',
                                      '$argc',
                                      '$argv',
                                      '$HTTP_POST_VARS',
                                      '$HTTP_GET_VARS',
                                      );

    private $assignations = array();

    private $php    = null;
    private $loader = null;
    private $loaderList = array('SplitGraphson',
                                'Collector',
                                'None',
                                );

    private $precedence   = null;
    private Php $phptokens;

    private $atomGroup = null;
    private $calls = null;
    private $theGlobals = array();

    private $namespace = '\\';
    private $uses       = null;
    private $filename   = null;

    private $links   = array();
    private $relicat = array();
    private $minId   = \PHP_INT_MAX;

    private $sequences     = null;

    private $currentMethod           = array();
    private $currentFunction         = array();
    private $currentVariables        = null;
    private $currentReturn           = null;
    private ClassTraitContext $currentClassTrait;
    private $currentProperties       = array();
    private $currentPropertiesCalls  = array();
    private $currentMethods          = array();
    private $currentMethodsCalls     = array();
    private $cases                   = null; // NestedCollector

    private $tokens = array();
    private $id     = 0;
    private $id0    = null;

    private $phpDocs    = array();
    private $attributes = array();

//    private $sqliteLocation = '/tmp/load.sqlite';
// for debug purpose
    private $sqliteLocation = ':memory:';

    public const ALTERNATIVE_SYNTAX = true;
    public const NORMAL_SYNTAX      = false;

    public const FULLCODE_SEQUENCE = ' /**/ ';
    public const FULLCODE_BLOCK    = ' { /**/ } ';
    public const FULLCODE_VOID     = ' ';

    public const ALIASED           = 1;
    public const NOT_ALIASED       = '';

    public const NO_LINE           = -1;

    public const VARIADIC          = true;
    public const NOT_VARIADIC      = false;

    public const FLEXIBLE          = true;
    public const NOT_FLEXIBLE      = false;

    public const REFERENCE         = true;
    public const NOT_REFERENCE     = false;

    public const BRACKET          = true;
    public const NOT_BRACKET      = false;

    public const ENCLOSING        = true;
    public const NO_ENCLOSING     = false;

    public const ALTERNATIVE      = true;
    public const NOT_ALTERNATIVE  = false;

    public const TRAILING         = true;
    public const NOT_TRAILING     = false;

#    public const NULLABLE         = true;
#    public const NOT_NULLABLE     = false;

    public const ELLIPSIS         = true;
    public const NOT_ELLIPSIS     = false;

    public const CLOSING_TAG      = true;
    public const NO_CLOSING_TAG   = false;

#    public const NO_VALUE          = -1;
    public const NOT_BINARY        = ''; // other values b, B (binary)

    public const ABSOLUTE     = true;
    public const NOT_ABSOLUTE = false;

    public const WITH_FULLNSPATH      = true;
    public const WITHOUT_FULLNSPATH   = false;

    public const CONSTANT_EXPRESSION       = true;
    public const NOT_CONSTANT_EXPRESSION   = false;

    public const FULLNSPATH_UNDEFINED = 'undefined';

    public const STANDALONE_BLOCK         = true;
    public const RELATED_BLOCK            = false;

    public const NO_NAMESPACE = '';

    public const CASE_SENSITIVE         = true;
    public const CASE_INSENSITIVE       = false;

    public const COMPILE_CHECK    = true;
    public const COMPILE_NO_CHECK = false;

    public const PROMOTED     = true;
    public const PROMOTED_NOT = false;

    public const READONLY      = true;
    public const NOSCREAM      = true;
    public const STATIC        = true;
    public const ABSTRACT      = true;
    public const FINAL         = true;

    private $contexts              = null;

    private $expressions         = array();
    private $atoms               = array();
    private $argumentsId         = array();
    private $sequence            = null;
    private $callsDatabase       = null;

    private $processing = array();

    private $plugins = array();

    private $stats = array('loc'       => 0,
                           'totalLoc'  => 0,
                           'files'     => 0,
                           'tokens'    => 0,
                          );

    private $atomVoid = null;
    private AnonymousNames $anonymousNames;
    protected $log = null;

    private $END_OF_EXPRESSION = array(); // that should be a constant

    public function __construct(bool $subtask = self::IS_NOT_SUBTASK) {
        parent::__construct($subtask);

        $this->atomGroup = new AtomGroup();

        $this->contexts  = new Context();

        $this->php = exakat('php');
        if (!$this->php->isValid()) {
            throw new InvalidPHPBinary($this->php->getConfiguration('phpversion'));
        }
        $tokens = $this->php->getTokens();
        $this->phptokens  = Php::getInstance($tokens);

        $this->assignations = array($this->phptokens::T_EQUAL,
                                    $this->phptokens::T_PLUS_EQUAL,
                                    $this->phptokens::T_AND_EQUAL,
                                    $this->phptokens::T_CONCAT_EQUAL,
                                    $this->phptokens::T_DIV_EQUAL,
                                    $this->phptokens::T_MINUS_EQUAL,
                                    $this->phptokens::T_MOD_EQUAL,
                                    $this->phptokens::T_MUL_EQUAL,
                                    $this->phptokens::T_OR_EQUAL,
                                    $this->phptokens::T_POW_EQUAL,
                                    $this->phptokens::T_SL_EQUAL,
                                    $this->phptokens::T_SR_EQUAL,
                                    $this->phptokens::T_XOR_EQUAL,
                                    $this->phptokens::T_COALESCE_EQUAL,
                                   );

        // Init all plugins here
        $this->plugins[] = new Boolval();
        $this->plugins[] = new Intval();
        $this->plugins[] = new Strval();
        $this->plugins[] = new Nullval();
        $this->plugins[] = new Constant();
        $this->plugins[] = new IsRead();
        $this->plugins[] = new IsModified();
        $this->plugins[] = new IsPhp();
        $this->plugins[] = new IsExt();
        $this->plugins[] = new IsStub();
        $this->plugins[] = new Encoding();

        $this->sequences = new Sequences();

        $this->currentVariables = new ContextVariables();

        $this->precedence = new Precedence(get_class($this->phptokens));

        $this->processing = array(
            $this->phptokens::T_OPEN_TAG                 => 'processOpenTag',
            $this->phptokens::T_OPEN_TAG_WITH_ECHO       => 'processOpenTag',

            $this->phptokens::T_DOLLAR                   => 'processDollar',
            $this->phptokens::T_VARIABLE                 => 'processVariable',
            $this->phptokens::T_LNUMBER                  => 'processInteger',
            $this->phptokens::T_DNUMBER                  => 'processFloat',

            $this->phptokens::T_OPEN_PARENTHESIS         => 'processParenthesis',

            $this->phptokens::T_PLUS                     => 'processAddition',
            $this->phptokens::T_MINUS                    => 'processAddition',
            $this->phptokens::T_STAR                     => 'processMultiplication',
            $this->phptokens::T_SLASH                    => 'processMultiplication',
            $this->phptokens::T_PERCENTAGE               => 'processMultiplication',
            $this->phptokens::T_POW                      => 'processPower',
            $this->phptokens::T_INSTANCEOF               => 'processInstanceof',
            $this->phptokens::T_SL                       => 'processBitshift',
            $this->phptokens::T_SR                       => 'processBitshift',

            $this->phptokens::T_DOUBLE_COLON             => 'processDoubleColon',
            $this->phptokens::T_OBJECT_OPERATOR          => 'processObjectOperator',
            $this->phptokens::T_NULLSAFE_OBJECT_OPERATOR => 'processObjectOperator',
            $this->phptokens::T_NEW                      => 'processNew',

            $this->phptokens::T_DOT                      => 'processDot',
            $this->phptokens::T_OPEN_CURLY               => 'processBlock',

            $this->phptokens::T_IS_SMALLER_OR_EQUAL      => 'processComparison',
            $this->phptokens::T_IS_GREATER_OR_EQUAL      => 'processComparison',
            $this->phptokens::T_GREATER                  => 'processComparison',
            $this->phptokens::T_SMALLER                  => 'processComparison',

            $this->phptokens::T_IS_EQUAL                 => 'processComparison',
            $this->phptokens::T_IS_NOT_EQUAL             => 'processComparison',
            $this->phptokens::T_IS_IDENTICAL             => 'processComparison',
            $this->phptokens::T_IS_NOT_IDENTICAL         => 'processComparison',
            $this->phptokens::T_SPACESHIP                => 'processSpaceship',

            $this->phptokens::T_OPEN_BRACKET             => 'processArrayLiteral',
            $this->phptokens::T_ARRAY                    => 'processArrayLiteral',
            $this->phptokens::T_UNSET                    => 'processIsset',
            $this->phptokens::T_ISSET                    => 'processIsset',
            $this->phptokens::T_EMPTY                    => 'processIsset',
            $this->phptokens::T_LIST                     => 'processString', // Can't move to processEcho, because of omissions
            $this->phptokens::T_EVAL                     => 'processIsset',
            $this->phptokens::T_ECHO                     => 'processEcho',
            $this->phptokens::T_EXIT                     => 'processExit',
            $this->phptokens::T_DOUBLE_ARROW             => 'processKeyvalue',

            $this->phptokens::T_HALT_COMPILER            => 'processHalt',
            $this->phptokens::T_PRINT                    => 'processPrint',
            $this->phptokens::T_INCLUDE                  => 'processPrint',
            $this->phptokens::T_INCLUDE_ONCE             => 'processPrint',
            $this->phptokens::T_REQUIRE                  => 'processPrint',
            $this->phptokens::T_REQUIRE_ONCE             => 'processPrint',
            $this->phptokens::T_RETURN                   => 'processReturn',
            $this->phptokens::T_THROW                    => 'processThrow',
            $this->phptokens::T_YIELD                    => 'processYield',
            $this->phptokens::T_YIELD_FROM               => 'processYieldfrom',

            $this->phptokens::T_EQUAL                    => 'processAssignation',
            $this->phptokens::T_PLUS_EQUAL               => 'processAssignation',
            $this->phptokens::T_AND_EQUAL                => 'processAssignation',
            $this->phptokens::T_CONCAT_EQUAL             => 'processAssignation',
            $this->phptokens::T_DIV_EQUAL                => 'processAssignation',
            $this->phptokens::T_MINUS_EQUAL              => 'processAssignation',
            $this->phptokens::T_MOD_EQUAL                => 'processAssignation',
            $this->phptokens::T_MUL_EQUAL                => 'processAssignation',
            $this->phptokens::T_OR_EQUAL                 => 'processAssignation',
            $this->phptokens::T_POW_EQUAL                => 'processAssignation',
            $this->phptokens::T_SL_EQUAL                 => 'processAssignation',
            $this->phptokens::T_SR_EQUAL                 => 'processAssignation',
            $this->phptokens::T_XOR_EQUAL                => 'processAssignation',
            $this->phptokens::T_COALESCE_EQUAL           => 'processAssignation',

            $this->phptokens::T_CONTINUE                 => 'processBreak',
            $this->phptokens::T_BREAK                    => 'processBreak',

            $this->phptokens::T_LOGICAL_AND              => 'processLogical',
            $this->phptokens::T_LOGICAL_XOR              => 'processLogical',
            $this->phptokens::T_LOGICAL_OR               => 'processLogical',
            $this->phptokens::T_XOR                      => 'processBitoperation',
            $this->phptokens::T_OR                       => 'processBitoperation',
            $this->phptokens::T_AND                      => 'processAnd',
            $this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG => 'processAnd', // &$var
            $this->phptokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG => 'processAnd', // &$var

            $this->phptokens::T_BOOLEAN_AND              => 'processLogical',
            $this->phptokens::T_BOOLEAN_OR               => 'processLogical',

            $this->phptokens::T_QUESTION                 => 'processTernary',
            $this->phptokens::T_NS_SEPARATOR             => 'processNsname',
            $this->phptokens::T_COALESCE                 => 'processCoalesce',

            $this->phptokens::T_INLINE_HTML              => 'processInlinehtml',

            $this->phptokens::T_INC                      => 'processPrePlusplus',
            $this->phptokens::T_DEC                      => 'processPrePlusplus',

            $this->phptokens::T_WHILE                    => 'processWhile',
            $this->phptokens::T_DO                       => 'processDo',
            $this->phptokens::T_IF                       => 'processIfthen',
            $this->phptokens::T_FOREACH                  => 'processForeach',
            $this->phptokens::T_FOR                      => 'processFor',
            $this->phptokens::T_TRY                      => 'processTry',
            $this->phptokens::T_CONST                    => 'processConst',
            $this->phptokens::T_SWITCH                   => 'processSwitch',
            $this->phptokens::T_MATCH                    => 'processMatch',
# Those are now integrated inside Switch
#            $this->phptokens::T_DEFAULT                  => 'processDefault',
#            $this->phptokens::T_CASE                     => 'processCase',
            $this->phptokens::T_CASE                     => 'processEnumCase',
            $this->phptokens::T_DECLARE                  => 'processDeclare',

            $this->phptokens::T_AT                       => 'processNoscream',
            $this->phptokens::T_CLONE                    => 'processClone',
            $this->phptokens::T_GOTO                     => 'processGoto',

            $this->phptokens::T_STRING                   => 'processString',
            $this->phptokens::T_NAME_QUALIFIED           => 'processString',
            $this->phptokens::T_NAME_RELATIVE            => 'processString',
            $this->phptokens::T_NAME_FULLY_QUALIFIED     => 'processString',
            $this->phptokens::T_STRING_VARNAME           => 'processString', // ${x} x is here
            $this->phptokens::T_CONSTANT_ENCAPSED_STRING => 'processLiteral',
            $this->phptokens::T_ENCAPSED_AND_WHITESPACE  => 'processLiteral',
            $this->phptokens::T_NUM_STRING               => 'processLiteral',

            $this->phptokens::T_ARRAY_CAST               => 'processCast',
            $this->phptokens::T_BOOL_CAST                => 'processCast',
            $this->phptokens::T_DOUBLE_CAST              => 'processCast',
            $this->phptokens::T_INT_CAST                 => 'processCast',
            $this->phptokens::T_OBJECT_CAST              => 'processCast',
            $this->phptokens::T_STRING_CAST              => 'processCast',
            $this->phptokens::T_UNSET_CAST               => 'processCast',

            $this->phptokens::T_FILE                     => 'processMagicConstant',
            $this->phptokens::T_CLASS_C                  => 'processMagicConstant',
            $this->phptokens::T_FUNC_C                   => 'processMagicConstant',
            $this->phptokens::T_LINE                     => 'processMagicConstant',
            $this->phptokens::T_DIR                      => 'processMagicConstant',
            $this->phptokens::T_METHOD_C                 => 'processMagicConstant',
            $this->phptokens::T_NS_C                     => 'processMagicConstant',
            $this->phptokens::T_TRAIT_C                  => 'processMagicConstant',

            $this->phptokens::T_BANG                     => 'processNot',
            $this->phptokens::T_TILDE                    => 'processNot',
            $this->phptokens::T_ELLIPSIS                 => 'processEllipsis',

            $this->phptokens::T_SEMICOLON                => 'processSemicolon',
            $this->phptokens::T_CLOSE_TAG                => 'processClosingTag',

            $this->phptokens::T_FUNCTION                 => 'processFunction',
            $this->phptokens::T_FN                       => 'processFn',
            $this->phptokens::T_CLASS                    => 'processClass',
            $this->phptokens::T_TRAIT                    => 'processTrait',
            $this->phptokens::T_INTERFACE                => 'processInterface',
            $this->phptokens::T_NAMESPACE                => 'processNamespace',
            $this->phptokens::T_USE                      => 'processUse',
            $this->phptokens::T_ENUM                     => 'processEnum',

            $this->phptokens::T_ABSTRACT                 => 'processAbstract',
            $this->phptokens::T_READONLY                 => 'processReadonly',
            $this->phptokens::T_FINAL                    => 'processFinal',
            $this->phptokens::T_PRIVATE                  => 'processPPP',
            $this->phptokens::T_PROTECTED                => 'processPPP',
            $this->phptokens::T_PUBLIC                   => 'processPPP',
            $this->phptokens::T_VAR                      => 'processVar',

            $this->phptokens::T_QUOTE                    => 'processQuote',
            $this->phptokens::T_START_HEREDOC            => 'processQuote',
            $this->phptokens::T_BACKTICK                 => 'processQuote',
            $this->phptokens::T_DOLLAR_OPEN_CURLY_BRACES => 'processDollarCurly',
            $this->phptokens::T_STATIC                   => 'processStatic',
            $this->phptokens::T_GLOBAL                   => 'processGlobalVariable',

            $this->phptokens::T_DOC_COMMENT              => 'processPhpdoc',
            $this->phptokens::T_ATTRIBUTE                => 'processAttribute',
        );

        $this->END_OF_EXPRESSION = array($this->phptokens::T_COMMA,
                                         $this->phptokens::T_CLOSE_PARENTHESIS,
                                         $this->phptokens::T_CLOSE_CURLY,
                                         $this->phptokens::T_SEMICOLON,
                                         $this->phptokens::T_CLOSE_BRACKET,
                                         $this->phptokens::T_CLOSE_TAG,
                                         $this->phptokens::T_COLON,
                                         $this->phptokens::T_DOUBLE_ARROW,
                                         $this->phptokens::T_SEMICOLON,
                                         );

        $this->cases = new NestedCollector();
        $this->log = new TimingLog('load.timing.csv');

        $this->anonymousNames = new AnonymousNames();
        $this->currentClassTrait = new ClassTraitContext();
     }

    public function __destruct() {
        $this->callsDatabase = null;
        $this->loader        = null;

        if (file_exists("{$this->config->projects_root}/projects/.exakat/calls.sqlite")) {
            unlink("{$this->config->projects_root}/projects/.exakat/calls.sqlite");
        }
    }

    public function runPlugins(AtomInterface $atom, array $linked = array()): void {
        foreach($this->plugins as $plugin) {
            try {
                $plugin->run($atom, $linked);
            } catch (\Throwable $t) {
                $this->log->log('Runplugin error : ' . $t->getMessage() . ' ' . $t->getFile() . ' ' . $t->getLine());
                display("Runplugin error\n");
            }
        }
    }

    public function run(): void {
        $this->logTime('Start');
        // Clean tmp folder
        $files = glob("{$this->config->tmp_dir}/*.csv");

        foreach($files as $file) {
            unlink($file);
        }

        $this->checkTokenLimit();

        // Reset Atom.
        $this->id0 = $this->addAtom('Project');
        $this->id0->code      = 'Whole';
        $this->id0->atom      = 'Project';
        $this->id0->code      = (string) $this->config->project;
        $this->id0->fullcode  = $this->config->project_name;
        $this->id0->token     = 'T_WHOLE';
        $this->atoms          = array();
        $this->minId          = \PHP_INT_MAX;

        $this->atomVoid = $this->addAtomVoid();

        // Cleaning the databases
        $this->datastore->cleanTable('tokenCounts');
        $this->datastore->cleanTable('dictionary');
        $this->logTime('Init');

        if ($filenames = $this->config->filename) {
            foreach($filenames as $filename) {
                if (!is_file($filename)) {
                    throw new MustBeAFile($filename);
                }

                try {
                    $this->callsDatabase = new \Sqlite3($this->sqliteLocation);
                    $this->calls = new Calls($this->callsDatabase);

                    $clientClass = "\\Exakat\\Loader\\{$this->config->loader}";
                    display("Loading with $clientClass\n");
                    if (!class_exists($clientClass)) {
                        throw new NoSuchLoader($clientClass, $this->loaderList);
                    }
                    $this->loader = new $clientClass($this->callsDatabase, $this->id0);

                    ++$this->stats['files'];
                    if ($this->processFile($filename, '')) {
                        $this->loader->finalize($this->relicat);
                    } else {
                        print "Error while loading the file.\n";
                    }
                } catch (NoFileToProcess $e) {
                    $this->datastore->ignoreFile($filename, $e->getMessage());
                    $this->log->log('Process File error : ' . $e->getMessage() . ' ' . $e->getFile() . ' ' . $e->getLine());
                    display('Process File error : ' . $e->getMessage() . ' ' . $e->getFile() . ' ' . $e->getLine());
                }
            }
        } elseif ($dirName = $this->config->dirname) {
            if (!is_dir($dirName)) {
                throw new MustBeADir($dirName);
            }
            $this->processDir($dirName);
        } elseif (($project = $this->config->project) !== 'default') {
            $this->processProject($project);
        } else {
            throw new \Exception('Default processing should not happen.');
        }

        $this->logTime('Load in graph');

        $stats = array(array('key' => 'loc',         'value' => $this->stats['loc']),
                       array('key' => 'locTotal',    'value' => $this->stats['totalLoc']),
                       array('key' => 'files',       'value' => $this->stats['files']),
                       array('key' => 'tokens',      'value' => $this->stats['tokens']),
                       );
        $this->datastore->addRow('hash', $stats);

        if ($this->stats['loc'] !== 0) {
            $this->datastore->addRow('hash', array('status' => 'Load'));

            $loadFinal = new LoadFinal();
            $this->logTime('LoadFinal new');
            $loadFinal->run();
            $this->logTime('The End');
        }
    }

    private function processProject(Project $project): array {
        $files = $this->datastore->getCol('files', 'file');

        if (empty($files)) {
            throw new NoFileToProcess((string) $project, "No file to load.\n");
        }

        $stubs = $this->config->stubs;

        display('Sequential processing');
        $this->runCollector($stubs);

        $this->gremlin = Graph::getConnexion();

        $nbTokens = $this->runProjectCore($files);

        return array('files'  => count($files),
                     'tokens' => $nbTokens);
    }

    private function runProjectCore(array $files): int {
        $clientClass = "\\Exakat\\Loader\\{$this->config->loader}";
        display("Loading with $clientClass\n");
        if (!class_exists($clientClass)) {
            throw new NoSuchLoader($clientClass, $this->loaderList);
        }

        $this->callsDatabase = new \Sqlite3($this->sqliteLocation);
        $this->loader = new $clientClass($this->callsDatabase, $this->id0);
        $this->calls = new Calls($this->callsDatabase);

        $version = $this->php->getVersion();
        $this->datastore->addRow('hash', array('notCompilable' . $version[0] . $version[2] => 0));

        $nbTokens = 0;
        if ($this->config->verbose && !$this->config->quiet) {
           $progressBar = new Progressbar(0, count($files), $this->config->screen_cols);
        }

        foreach($files as $file) {
            try {
                ++$this->stats['files'];
                $r = $this->processFile($file, $this->config->code_dir);
                $nbTokens += $r;
                if (isset($progressBar)) {
                    echo $progressBar->advance();
                }
            } catch (NoFileToProcess $e) {
                $this->datastore->ignoreFile($file, $e->getMessage());
                if (isset($progressBar)) {
                    echo $progressBar->advance();
                }
            }
            // Reduce memory as Atoms are not kept between files.
            gc_collect_cycles();
        }
        $this->loader->finalize($this->relicat);

        return $nbTokens;
    }

    private function runCollector(array $omittedFiles): void {
        $this->callsDatabase = new \Sqlite3($this->sqliteLocation);
        $this->loader = new Collector($this->callsDatabase, $this->id0);
        $this->calls = new Calls($this->callsDatabase);

        $fileExtensions = $this->config->file_extensions;
        $atomGroup = clone $this->atomGroup;

        $stats = $this->stats;
        foreach($omittedFiles as $file) {
            try {
                $ext = pathinfo($file, PATHINFO_EXTENSION);
                if (!in_array($ext, $fileExtensions, \STRICT_COMPARISON)) {
                    continue;
                }

                $this->processFile($file, $this->config->code_dir, self::COMPILE_NO_CHECK);
            } catch (NoFileToProcess $e2) {
                // Ignore
            }
        }
        $this->loader->finalize($this->relicat);
        $this->atomGroup = $atomGroup;

        $this->theGlobals = array();

        $this->stats = $stats;
    }

    private function processDir(string $dir): array {
        if (!file_exists($dir)) {
            return array('files'  => -1,
                         'tokens' => -1);
        }

        $set = new All($dir);
        $set->addFilter(new Filenames($this->config->dir_root));
        $set->addFilter(new FileExtensions($this->config->file_extensions));
        $set->addFilter(new IgnoreDirs($this->config->ignore_dirs, $this->config->include_dirs));

        $files = $set->getFiles();
        $ignoredFiles = $set->getIgnored();

        $clientClass = "\\Exakat\\Loader\\{$this->config->loader}";
        display("Loading with $clientClass\n");
        if (!class_exists($clientClass)) {
            throw new NoSuchLoader($clientClass, $this->loaderList);
        }
        $this->callsDatabase = new \Sqlite3($this->sqliteLocation);
        $this->calls = new Calls($this->callsDatabase);
        $this->loader = new $clientClass($this->callsDatabase, $this->id0);

        $nbTokens = 0;
        foreach($files as $file) {
            try {
                ++$this->stats['files'];
                $r = $this->processFile($file, $dir);
                $nbTokens += $r;
            } catch (NoFileToProcess $e) {
                $this->datastore->ignoreFile($file, $e->getMessage());
            }
        }
        $this->loader->finalize($this->relicat);

        $this->loader = new Collector($this->callsDatabase, $this->id0);
        $stats = $this->stats;
        foreach($ignoredFiles as $file) {
            try {
                $this->processFile($file, $dir);
            } catch (NoFileToProcess $e) {
                $this->datastore->ignoreFile($file, $e->getMessage());
            }
        }
        $this->loader->finalize($this->relicat);
        $this->stats = $stats;

        return array('files'  => count($files),
                     'tokens' => $nbTokens);
    }

    private function reset(): void {
        $this->atoms   = array();
        $this->links   = array();
        $this->minId  = \PHP_INT_MAX;

        $this->contexts    = new Context();
        $this->expressions = array();
        $this->uses        = new Fullnspaths();

        $this->currentMethod           = array();
        $this->currentFunction         = array();
        $this->currentClassTrait       = new ClassTraitContext();
        $this->currentVariables        = new ContextVariables();

        $this->tokens                  = array();
        $this->phpDocs                 = array();
        $this->attributes              = array();
    }

    public function initDiff(): void {
        $clientClass = "\\Exakat\\Loader\\{$this->config->loader}";
        display("Loading with $clientClass\n");
        if (!class_exists($clientClass)) {
            throw new NoSuchLoader($clientClass, $this->loaderList);
        }

        $res = $this->gremlin->query('g.V().id().max()');
        $this->atomGroup = new AtomGroup($res->toInt() + 1);

        $this->id0 = $this->addAtom('Project');
        $this->id0->code      = 'Whole';
        $this->id0->atom      = 'Project';
        $this->id0->code      = (string) $this->config->project;
        $this->id0->fullcode  = $this->config->project_name;
        $this->id0->token     = 'T_WHOLE';
        $this->atoms          = array();
        $this->minId         = \PHP_INT_MAX;

        $this->loader = new $clientClass($this->callsDatabase, $this->id0);
    }

    public function finishDiff(): void {
        $this->loader->finalize(array());

        $loadFinal = new LoadFinal();
        $this->logTime('LoadFinal new');
        $loadFinal->run();
        $this->logTime('The End');

        $this->reset();
    }

    public function processDiffFile(string $filename, string $path): void {
        try {
            $this->processFile($filename, $path);
        } catch(NoFileToProcess $e ) {
            $this->datastore->ignoreFile($filename, $e->getMessage());
        }
    }

    private function processFile(string $filename, string $path, bool $compileCheck = self::COMPILE_CHECK): int {
        $timer = new Timer();
        $fullpath = $path . $filename;

        $this->filename = $filename;

        $log = array();

        if (is_link($fullpath)) {
            return 0;
        }
        if (!file_exists($fullpath)) {
            throw new NoFileToProcess($filename, 'unreachable file');
        }

        if (filesize($fullpath) === 0) {
            throw new NoFileToProcess($filename, 'empty file');
        }

        if ($compileCheck === self::COMPILE_CHECK && !$this->php->compile($fullpath)) {
            $error = $this->php->getError();
            $error['file'] = $filename;

            $version = $this->php->getVersion();
            $this->datastore->addRow('compilation' . $version[0] . $version[2], array($error));

            $count = $this->datastore->gethash('notCompilable' . $version[0] . $version[2]);
            $this->datastore->addRow('hash', array('notCompilable' . $version[0] . $version[2] => (int) $count + 1));

            return 0;
        }

        $tokens = $this->php->getTokenFromFile($fullpath);
        $log['token_initial'] = count($tokens);

        if (count($tokens) < 3) {
            throw new NoFileToProcess($filename, 'Only ' . count($tokens) . ' tokens');
        }

        $comments     = 0;
        $this->tokens = array();
        $total        = 0;
        $line         = 0;
        $ws           = '';
        foreach($tokens as $position => $t) {
            if (is_array($t)) {
                switch($t[0]) {
                    case $this->phptokens::T_WHITESPACE:
                        $line += substr_count($t[1], "\n");
                        $ws   .= $t[1];
                        break;

                    case $this->phptokens::T_COMMENT :
                        $c        = substr_count($t[1], "\n");
                        $line     += $c;
                        $comments += $c;
                        $ws       .= $t[1];
                        break;

                    case $this->phptokens::T_BAD_CHARACTER :
                        // Ignore all
                        break;

                    case $this->phptokens::T_DOC_COMMENT:
                        $t[] = $position;
                        $t[] = '';
                        $ws  = &$t[4];
                        $this->tokens[] = $t;
                        $comments += substr_count($t[1], "\n") + 1;
                        break;

                    default :
                        $t[] = $position;
                        $t[] = '';
                        $ws  = &$t[4];
                        $line = $t[2];
                        $this->tokens[] = $t;
                        ++$total;
                    }
            } elseif (is_string($t)) {
                $token = array($this->phptokens::TOKENS[$t],
                               $t,
                               $line,
                               $position,
                               '',
                               );
                $ws             = &$token[4];
                $this->tokens[] = $token;
                ++$total;
            } else {
                assert(false, "$t is in a wrong token type : " . gettype($t));
            }
        }
        $this->stats['loc'] -= $comments;

        // Final token
        $this->tokens[] = array(0 => $this->phptokens::T_END,
                                1 => '/* END */',
                                2 => $line,
                                3 => 0,
                                4 => '');
        $this->stats['tokens'] += count($tokens);
        unset($tokens);

        $this->uses   = new Fullnspaths();

        $id1 = $this->addAtom('File');
        $id1->code     = $filename;
        $id1->fullcode = $filename;
        $id1->token    = 'T_FILENAME';

        $this->currentMethod           = array($id1);
        $this->currentFunction         = array($id1);

        try {
            $n = count($this->tokens) - 2;
            $this->id = 0; // set to 0 so as to calculate line in the next call.
            $this->startSequence(); // At least, one sequence available
            $this->sequence->ws->opening = '';
            $this->sequence->ws->closing = '';

            $this->id = -1;
            do {
                $theExpression = $this->processNext();
                $this->addToSequence($theExpression);
            } while ($this->id < $n);

            $sequence = $this->sequence;
            $sequence->ws->separators[] = '';

            $this->addLink($id1, $sequence, 'FILE');
        } catch (LoadError $e) {
            if ($compileCheck === self::COMPILE_CHECK) {
                $this->log->log('Can\'t process file \'' . $this->filename . '\' during load (\'' . $this->tokens[$this->id][0] . '\', line \'' . $this->tokens[$this->id][2] . '\'). Ignoring' . PHP_EOL . $e->getMessage() . PHP_EOL);
            }
            $this->reset();
            $this->calls->reset();
            throw new NoFileToProcess($filename, 'empty (1)', 0, $e);
        } catch (\Throwable $e) {
            print 'Error message : ' . $e->getMessage() . ' on line ' . $e->getLine();
            die();
        } finally {
            try {
                $this->checkTokens($filename);
                $this->calls->save();
            } catch (LoadError $e) {
                $this->log->log('Can\'t process file \'' . $this->filename . '\' during load (finally) (\'' . $this->tokens[$this->id][0] . '\', line \'' . $this->tokens[$this->id][2] . '\'). Ignoring' . PHP_EOL . $e->getMessage() . PHP_EOL);
                $this->reset();
                $this->calls->reset();
                throw new NoFileToProcess($filename, 'empty (2)', 0, $e);
            }

            $this->stats['totalLoc'] += $line;
            $this->stats['loc'] += $line;
        }

        $timer->end();
        $load = $timer->duration(Timer::MS);

        $atoms = count($this->atoms);
        $links = count($this->links);

        $timer = new Timer();
        $this->saveFiles();
        $timer->end();
        $save = $timer->duration(Timer::MS);

        $this->log->log("$filename\t$load\t$save\t$log[token_initial]\t$atoms\t$links");

        return $log['token_initial'];
    }

    private function processNext(): AtomInterface {
        $this->moveToNext();

        if ($this->nextIs(array($this->phptokens::T_END), 0)          ||
            !isset($this->processing[ $this->tokens[$this->id][0] ])) {
            display("Can't process file '$this->filename' during load ('{$this->tokens[$this->id][0]}', line {$this->tokens[$this->id][2]}), token {$this->tokens[$this->id][0]}) : {$this->tokens[$this->id][1]}). Ignoring\n");
            $this->log->log("Can't process file '$this->filename' during load ('{$this->tokens[$this->id][0]}', line {$this->tokens[$this->id][2]}). Ignoring\n");

            throw new LoadError('Processing error (processNext end)');
        }
        $method = $this->processing[ $this->tokens[$this->id][0] ];

//        print "  $method in".PHP_EOL;
        $atom = $this->$method();
//        print "  $method out ".PHP_EOL;

        return $atom;
    }

    private function processExpression(array $finals): AtomInterface {
        do {
           $expression = $this->processNext();
           $this->checkPhpdoc();
        } while (!$this->nextIs($finals));

        $this->popExpression();

        return $expression;
    }

    private function processColon(): AtomInterface {
        $current = $this->id;
        --$this->id;
        $tag = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        $this->moveToNext();

        $label = $this->addAtom('Gotolabel', $this->id);
        $this->addLink($label, $tag, 'GOTOLABEL');
        $label->fullcode = $tag->fullcode . ' :';
        $label->ws->opening = $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];

        $method = empty($this->currentFunction) ? '' : end($this->currentFunction)->fullnspath;

        $class = $this->currentClassTrait->getCurrent()->fullnspath ?? '\global';
        $this->calls->addDefinition(Calls::GOTO, $class . "::$method..$tag->fullcode", $label);

        $this->addToSequence($label);
        $this->sequence->ws->separators[] = '';

        return $label;
    }

    //////////////////////////////////////////////////////
    /// processing complex tokens
    //////////////////////////////////////////////////////
    private function processQuote(): AtomInterface {
        $current = $this->id;
        $fullcode = array();
        $rank = -1;
        $elements = array();

        if ($this->tokens[$current][0] === $this->phptokens::T_QUOTE) {
            $string = $this->addAtom('String', $current);
            $finalToken = $this->phptokens::T_QUOTE;
            $closeQuote = '"';
            $type = $this->phptokens::T_QUOTE;

            $openQuote = $this->tokens[$this->id][1];
            if ($this->tokens[$current][1][0] === 'b' || $this->tokens[$current][1][0] === 'B') {
                $string->binaryString = $openQuote[0];
                $openQuote = '"';
            }
        } elseif ($this->tokens[$current][0] === $this->phptokens::T_BACKTICK) {
            $string = $this->addAtom('Shell', $current);
            $finalToken = $this->phptokens::T_BACKTICK;
            $openQuote = '`';
            $closeQuote = '`';
            $type = $this->phptokens::T_BACKTICK;
        } elseif ($this->tokens[$current][0] === $this->phptokens::T_START_HEREDOC) {
            $string = $this->addAtom('Heredoc', $current);
            $finalToken = $this->phptokens::T_END_HEREDOC;
            $openQuote = $this->tokens[$this->id][1];
            if (strtolower($openQuote[0]) === 'b') {
                $string->binaryString = $openQuote[0];
                $openQuote = substr($openQuote, 1);
            }

            $closeQuote = $openQuote[3] === "'" ? substr($openQuote, 4, -2) : substr($openQuote, 3);

            $type = $this->phptokens::T_START_HEREDOC;
        } else {
            throw new LoadError(__METHOD__ . ' : unsupported type of open quote : ' . $this->tokens[$current][0]);
        }

        // Set default, in case the whole loop is skipped
        $string->noDelimiter = '';
        $string->delimiter   = '';

        while ($this->tokens[$this->id + 1][0] !== $finalToken) {
            $currentVariable = $this->id + 1;
            if ($this->nextIs(array($this->phptokens::T_CURLY_OPEN))) {
                $open = $this->id + 1;
                $this->moveToNext(); // Skip {
                do {
                    $part = $this->processNext();
                } while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY)));
                $this->moveToNext(); // Skip }

                $this->popExpression();

                $part->enclosing = self::ENCLOSING;
                $part->fullcode  = $this->tokens[$open][1] . $part->fullcode . '}';
                $part->token     = $this->getToken($this->tokens[$currentVariable][0]);
                $part->bracket   = self::BRACKET;

                $this->pushExpression($part);

                $elements[] = $part;
            } elseif ($this->nextIs(array($this->phptokens::T_DOLLAR_OPEN_CURLY_BRACES))) {
                $part = $this->processDollarCurly();

                $part->enclosing = self::ENCLOSING;
                $part->token     = $this->getToken($this->tokens[$currentVariable][0]);
                $this->pushExpression($part);

                $elements[] = $part;
            } elseif ($this->nextIs(array($this->phptokens::T_VARIABLE))) {
                if ($this->tokens[$this->id + 1][1] === '$this') {
                    $atom = 'This';
                } elseif (in_array($this->tokens[$this->id + 1][1], $this->PHP_SUPERGLOBALS, \STRICT_COMPARISON)) {
                    $atom = 'Phpvariable';
                } elseif ($this->nextIs(array($this->phptokens::T_OBJECT_OPERATOR,
                                              $this->phptokens::T_NULLSAFE_OBJECT_OPERATOR), 2)) {
                    $atom = 'Variableobject';
                } elseif ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET), 2)) {
                    $atom = 'Variablearray';
                } else {
                    $atom = 'Variable';
                }
                $this->moveToNext();
                $variable = $this->processSingle($atom);
                $variable->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

                if ($atom === 'This' && ($class = $this->currentClassTrait->getCurrent())) {
                    $variable->fullnspath = $class->fullnspath;
                    $this->calls->addCall(Calls::A_CLASS, $class->fullnspath, $variable);
                }

                if ($this->nextIs(array($this->phptokens::T_OBJECT_OPERATOR,
                                        $this->phptokens::T_NULLSAFE_OBJECT_OPERATOR,
                                        ))) {
                    $this->moveToNext();
                    $property = $this->addAtom('Member', $this->id);
                    $property->ws->operator = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

                    $propertyName = $this->processNextAsIdentifier();

                    $property->fullcode  = "{$variable->fullcode}->{$propertyName->fullcode}";
                    $property->enclosing = self::NO_ENCLOSING;

                    $this->addLink($property, $variable, 'OBJECT');
                    $this->addLink($property, $propertyName, 'MEMBER');
                    $this->runPlugins($property, array('OBJECT' => $variable,
                                                       'MEMBER' => $propertyName,
                                                       ));

                    if ($variable->atom === 'This' &&
                        $propertyName->token   === 'T_STRING') {
                        $this->calls->addCall(Calls::PROPERTY, "{$variable->fullnspath}::{$propertyName->code}", $property);
                        array_collect_by($this->currentPropertiesCalls, $propertyName->code, $property);
                    }

                    $this->pushExpression($property);

                    $elements[] = $property;
                } elseif ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET))) {
                    $this->moveToNext(); // Skip $a
                    $array = $this->addAtom('Array', $this->id);
                    $array->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                    $this->moveToNext(); // Skip [

                    if ($this->nextIs(array($this->phptokens::T_NUM_STRING), 0)) {
                        $index = $this->processSingle('Integer');
                        $this->runPlugins($index);
                    } elseif ($this->nextIs(array($this->phptokens::T_MINUS), 0)) {
                        $this->moveToNext();
                        if ($this->tokens[$this->id][1][0] === '0') {
                            $index            = $this->processSingle('String');
                            $index->code      = "-{$index->code}";
                            $index->fullcode  = "-{$index->fullcode}";
                        } else {
                            $index            = $this->processSingle('Integer');
                            $index->code      = (string) (-1 * $index->code);
                            $index->fullcode  = (string) (-1 * $index->fullcode);
                        }
                    } elseif ($this->nextIs(array($this->phptokens::T_STRING), 0)) {
                        $index = $this->processSingle('String');
                        $index->ws->opening = '';
                        $index->ws->closing = '';
                    } elseif ($this->nextIs(array($this->phptokens::T_VARIABLE), 0)) {
                        $index = $this->processVariable();
                        $this->popExpression();
                    } else {
                        throw new UnknownCase('Couldn\'t read that token inside quotes : ' . $this->tokens[$this->id][0]);
                    }
                    $this->moveToNext(); // Skip ]
                    $array->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

                    $array->fullcode  = "{$variable->fullcode}[{$index->fullcode}]";
                    $array->enclosing = self::NO_ENCLOSING;

                    $this->addLink($array, $variable, 'VARIABLE');
                    $this->addLink($array, $index, 'INDEX');
                    $this->runPlugins($array, array('VARIABLE' => $variable,
                                                    'INDEX'    => $index,
                                                     ));

                    $this->pushExpression($array);
                    $elements[] = $array;
                } else {
                    $this->pushExpression($variable);
                }
            } else {
                $this->processNext();
            }

            $part = $this->popExpression();
            if ($part->atom === 'String') {
                $part->noDelimiter = $part->code;
                $part->delimiter   = '';
                $part->ws->opening = '';
            } else {
                $part->noDelimiter = '';
                $part->delimiter   = '';
            }
            $part->rank = ++$rank;
            $fullcode[] = $part->fullcode;
            $elements[] = $part;

            $this->addLink($string, $part, 'CONCAT');
        }

        if ($type === $this->phptokens::T_START_HEREDOC) {
            /*
            This might be a PHP 7 artefact. Keep it here until checked.
            if (!empty($elements)) {
                // This is the last part
                $part = array_pop($elements);
                $part->noDelimiter = rtrim($part->noDelimiter, "\n");
                $part->code        = rtrim($part->code,        "\n");
                $part->fullcode    = rtrim($part->fullcode,    "\n");
                $elements[]        = $part;
            }*/
            // Get the closing quote for flexibility
            $closeQuote = $this->tokens[$this->id + 1][1];
            if (trim($closeQuote) !== $closeQuote) {
                $string->flexible = self::FLEXIBLE;
            }
        }

        $this->moveToNext();
        $string->fullcode    = $string->binaryString . $openQuote . implode('', $fullcode) . $closeQuote;
        $string->noDelimiter = implode('', $fullcode);
        $string->delimiter   = $openQuote;
        $string->count       = $rank + 1;

        $string->ws->opening = $openQuote;
        $string->ws->closing = $closeQuote . $this->tokens[$this->id][4];

        if ($type === $this->phptokens::T_START_HEREDOC) {
            $string->delimiter = trim($closeQuote);
            $string->heredoc   = $openQuote[3] !== "'";
        }

        $this->runPlugins($string, $elements);
        $this->pushExpression($string);

        if ($type === $this->phptokens::T_QUOTE) {
            $string = $this->processFCOA($string);
        }

        $this->checkExpression();

        return $string;
    }

    private function processDollarCurly(): AtomInterface {
        $current = $this->id;
        $atom = $this->nextIs(array($this->phptokens::T_GLOBAL), -1) ? 'Globaldefinition' : 'Variable';
        $variable = $this->addAtom($atom, $current);

        $this->moveToNext(); // Skip ${
        do {
            $name = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY)));
        $this->moveToNext(); // Skip }

        $this->popExpression();
        $this->addLink($variable, $name, 'NAME');

        $variable->fullcode  = '${' . $name->fullcode . '}';
        $variable->enclosing = self::ENCLOSING;
        $variable->ws->opening = $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
        $variable->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->runPlugins($variable, array('NAME' => $name));

        $this->checkExpression();

        return $variable;
    }

    private function processTry(): AtomInterface {
        $current = $this->id;
        $try = $this->addAtom('Try', $current);
        $try->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $block = $this->processFollowingBlock(array($this->phptokens::T_CLOSE_CURLY));
        $try->ws->closing = '';

        $this->addLink($try, $block, 'BLOCK');
        $extras = array('BLOCK' => $block);

        $rank = 0;
        $fullcode = array();
        $this->checkPhpdoc();
        while ($this->nextIs(array($this->phptokens::T_CATCH))) {
            $catchId = $this->id + 1;
            $this->moveToNext(); // Skip catch
            $this->moveToNext(); // Skip (

            $catch = $this->addAtom('Catch', $catchId);
            $catchFullcode = array();
            $extrasCatch = array();
            $rankCatch = -1;
            $catch->ws->opening = $this->tokens[$this->id - 1][1] . $this->tokens[$this->id - 1][4] .
                                  $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            // processing the typehint (including multiple types)
            $rankClass = -1;
            while (!$this->nextIs(array($this->phptokens::T_VARIABLE,
                                        $this->phptokens::T_CLOSE_PARENTHESIS))) {
                $class = $this->processOneNsname();
                $this->addLink($catch, $class, 'CLASS');
                $class->rank = ++$rankClass;

                $this->calls->addCall(Calls::A_CLASS, $class->fullnspath, $class);
                $catchFullcode[] = $class->fullcode;
                $extrasCatch['CLASS' . $rankCatch] = $class;

                if ($this->nextIs(array($this->phptokens::T_OR))) {
                    $catch->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
                    $this->moveToNext(); // Skip |
                } else {
                    $catch->ws->separators[] = '';
                }
            }
            $catch->rank = ++$rankCatch;
            $catch->count = $rankClass + 1;
            $catchFullcode = implode(' | ', $catchFullcode);

            // Process variable
            if ($this->nextIs(array($this->phptokens::T_VARIABLE))) {
                $variable = $this->processNext();

                $this->popExpression();
                $this->addLink($catch, $variable, 'VARIABLE');
                $extrasCatch['VARIABLE'] = $variable;

                $variableFullcode = $variable->fullcode;
            } else {
                $variableFullcode = '';
            }

            // Skip )
            $this->moveToNext();
            $catch->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            // Skip }
            $blockCatch = $this->processFollowingBlock(array($this->phptokens::T_CLOSE_CURLY));
            $catch->ws->closing = '';

            $this->addLink($catch, $blockCatch, 'BLOCK');
            $extrasCatch['BLOCK'] = $blockCatch;

            $catch->fullcode = $this->tokens[$catchId][1] . ' (' . $catchFullcode . ' ' . $variableFullcode . ')' . static::FULLCODE_BLOCK;
            $catch->rank     = ++$rank;

            $this->addLink($try, $catch, 'CATCH');
            $fullcode[] = $catch->fullcode;

            $extras['CATCH' . $rank] = $catch;
            $this->runPlugins($catch, $extrasCatch);
            $this->checkPhpdoc();
        }

        $this->checkPhpdoc();
        if ($this->nextIs(array($this->phptokens::T_FINALLY))) {
            $finally = $this->processFinally();

            $this->addLink($try, $finally, 'FINALLY');
        }

        $try->fullcode = $this->tokens[$current][1] . static::FULLCODE_BLOCK . implode('', $fullcode) . ( isset($finally) ? $finally->fullcode : '');
        $try->count    = $rank;

        $this->addToSequence($try);
        $this->sequence->ws->separators[] = '';

        $this->runPlugins($try, $extras);
        return $try;
    }

    private function processFinally(): AtomInterface {
        $finallyId = $this->id + 1;
        $finally = $this->addAtom('Finally', $finallyId);
        $finally->ws->opening = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->moveToNext();
        $finallyBlock = $this->processFollowingBlock(array($this->phptokens::T_CLOSE_CURLY));
        $this->addLink($finally, $finallyBlock, 'BLOCK');

        $finally->ws->closing = '';
        $finally->fullcode = $this->tokens[$finallyId][1] . static::FULLCODE_BLOCK;

        $this->runPlugins($finally, array('BLOCK' => $finallyBlock));

        return $finally;
    }

    private function processFn(): AtomInterface {
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_AND,
                                $this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG))) {
            $this->moveToNext();
            $reference = self::REFERENCE;
        } else {
            $reference = self::NOT_REFERENCE;
        }

        $this->moveToNext();
        $atom     = 'Arrowfunction';

        // Keep a copy of the current variables, to remove the arguments when we are done
        $previousContextVariables = clone $this->currentVariables;

        $fn              = $this->processParameters($atom);
        $fn->reference   = $reference;
        $fn->position    = $this->tokens[$current][3];
        $fn->code        = $this->tokens[$current][1];
        $fn->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4] .
                           $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];

        // Process return type
        $fn->ws->endargs = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $returnTypeFullcode = $this->processTypehint($fn);

        $fn->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        $this->moveToNext($this->phptokens::T_DOUBLE_ARROW); // skip =>

        $this->contexts->nestContext(Context::CONTEXT_FUNCTION);
        $this->contexts->toggleContext(Context::CONTEXT_FUNCTION);

        // arrowfunction may be static
        if ($this->tokens[$current - 1][0] === $this->phptokens::T_STATIC) {
            $this->currentClassTrait->pushContext(ClassTraitContext::NO_CLASS_TRAIT_CONTEXT);
        }

        $block = $this->processExpression($this->END_OF_EXPRESSION);

        // arrowfunction may be static
        if ($this->tokens[$current - 1][0] === $this->phptokens::T_STATIC) {
            $this->currentClassTrait->popContext();
        }

        $this->contexts->exitContext(Context::CONTEXT_FUNCTION);

        $this->addLink($fn, $block, 'BLOCK');
        $this->addLink($fn, $block, 'RETURNED');
        $this->addLink($fn, $block, 'RETURN');
        $this->makeAttributes($fn);

        $fn->token    = $this->getToken($this->tokens[$current][0]);
        $fn->fullcode = $this->tokens[$current][1] . ' ' .
                        ($fn->reference ? '&' : '') .
                        '(' . $fn->fullcode . ')' .
                        $returnTypeFullcode .
                        ' => ' . $block->fullcode;
        $fn->fullnspath = $this->anonymousNames->getName(AnonymousNames::A_ARROW_FUNCTION);

        $this->currentVariables = $previousContextVariables;

        $this->pushExpression($fn);
        $this->checkExpression();

        return $fn;
    }

    private function processFunction(): AtomInterface {
        $current = $this->id;

        if ( $this->contexts->isContext(Context::CONTEXT_CLASS) &&
             !$this->contexts->isContext(Context::CONTEXT_FUNCTION)) {

            if (in_array(mb_strtolower($this->tokens[$this->id + 1][1]),
                         array('__construct',
                               '__destruct',
                               '__call',
                               '__callstatic',
                               '__get',
                               '__set',
                               '__isset',
                               '__unset',
                               '__sleep',
                               '__wakeup',
                               '__tostring',
                               '__invoke',
                               '__set_state',
                               '__clone',
                               '__debuginfo',
                               '__serialize',
                               '__unserialize',
                               ),
                            \STRICT_COMPARISON)) {
                $atom = 'Magicmethod';

            } else {
                $atom = 'Method';
            }
        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            $atom = 'Closure';
        } elseif ($this->nextis(array($this->phptokens::T_AND,
                                      $this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG)) &&
                  $this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS), 2)) {
            $atom = 'Closure';
        } else {
            $atom = 'Function';
        }

        $this->contexts->nestContext(Context::CONTEXT_CLASS);
        $this->contexts->nestContext(Context::CONTEXT_FUNCTION);
        $this->contexts->toggleContext(Context::CONTEXT_FUNCTION);

        $previousContextVariables = $this->currentVariables;
        $this->currentVariables = new ContextVariables();

        if ($this->nextIs(array($this->phptokens::T_AND,
                                $this->phptokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
                                $this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
                                ))) {
            $this->moveToNext();
            $referenceWS = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            $reference = self::REFERENCE;
        } else {
            $referenceWS = '';
            $reference = self::NOT_REFERENCE;
        }

        if ($atom !== 'Closure') {
            $name = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        }
        $this->moveToNext();
        $toargsWS = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        // Process arguments
        $function              = $this->processParameters($atom);
        $function->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4] .
                                 $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
        $function->ws->toargs  = $toargsWS;

        $function->position = $this->tokens[$current][3];
        $function->code = $function->atom === 'Closure' ? 'function' : $name->fullcode;
        $function->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        if ($function->atom === 'Function') {
            $this->getFullnspath($name, 'function', $function);
            $this->calls->addDefinition(Calls::FUNCTION, $function->fullnspath, $function);

            $this->addLink($function, $name, 'NAME');
        } elseif ($function->atom === 'Closure') {
            $function->fullnspath = $this->anonymousNames->getName(AnonymousNames::A_FUNCTION);

            // closure may be static
            if ($this->tokens[$current - 1][0] === $this->phptokens::T_STATIC) {
                $this->currentClassTrait->pushContext(ClassTraitContext::NO_CLASS_TRAIT_CONTEXT);
            }
        } elseif ($function->isA(array('Method', 'Magicmethod'))) {
            $function->fullnspath = $this->currentClassTrait->getCurrent()->fullnspath . '::' . mb_strtolower($name->code);

            if (empty($function->visibility)) {
                $function->visibility = 'none';
            }

            $this->addLink($function, $name, 'NAME');
        } else {
            throw new LoadError(__METHOD__ . ' : wrong type of function ' . $function->atom);
        }

        $function->token   = $this->getToken($this->tokens[$current][0]);

        $argumentsFullcode = $function->fullcode;
        $function->reference = $reference;

        $function->ws->reference = $referenceWS;
        $function->ws->endargs = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        // Process use
        $useFullcode = array();
        if ($this->nextIs(array($this->phptokens::T_USE))) {

            $function->ws->touse = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4] .
                                   $this->tokens[$this->id + 2][1] . $this->tokens[$this->id + 2][4];
            $this->moveToNext(); // Skip use
            $this->moveToNext(); // Skip (

            $rank = 0;
            $uses = array();
            do {
                $this->checkPhpDoc();
                $this->moveToNext(); // Skip ( or ,

                if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS), 0)) {
                    $useFullcode[] = '';

                    continue;
                }

                if ($this->nextis(array($this->phptokens::T_AND,
                                        $this->phptokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG,
                                        $this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG,
                                        ), 0)) {
                    $this->moveToNext();
                    $arg = $this->processSingle('Parameter');
                    $arg->ws->reference = $this->tokens[$this->id - 1][1] . $this->tokens[$this->id - 1][4];
                    $arg->reference = self::REFERENCE;
                    $arg->fullcode = "&$arg->fullcode";
                } else {
                    $arg = $this->processSingle('Parameter');
                }
                $this->moveToNext();
                $uses[] = $arg;

                $useFullcode[] = $arg->fullcode;
                $arg->rank = ++$rank;

                $this->addLink($function, $arg, 'USE');
                $this->currentVariables->set($arg->code, $arg);
                if ($previousContextVariables->exists($arg->code)) {
                    $this->addLink($previousContextVariables->get($arg->code), $arg, 'DEFINITION');
                }

                if ($this->nextIs(array($this->phptokens::T_COMMA), 0)) {
                    $function->ws->touseseparators[] = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                } else {
                    $function->ws->touseseparators[] = $this->tokens[$this->id - 1][4] .
                                                       $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                }
            } while ($this->nextIs(array($this->phptokens::T_COMMA), 0));
            $this->runPlugins($function, array('USE' => $uses));
        }

        // Process return type
        $returnTypes = $this->processTypehint($function);
        $function->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        // Process block
        if ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {
            $block = $this->addAtomVoid();
            $block->ws->opening = ';' . $this->tokens[$this->id + 1][4];
            $this->addLink($function, $block, 'BLOCK');
            $this->moveToNext(); // skip the next ;
            $blockFullcode = ' ;';
            $this->runPlugins($block);
        } else {
            $block = $this->processFollowingBlock(array($this->phptokens::T_CLOSE_CURLY));
            $this->addLink($function, $block, 'BLOCK');
            $blockFullcode = self::FULLCODE_BLOCK;
        }

        $function->fullcode   = $this->tokens[$current][1] . ' ' . ($function->reference ? '&' : '') .
                                ($function->atom === 'Closure' ? '' : $name->fullcode) . '(' . $argumentsFullcode . ')' .
                                (empty($useFullcode) ? '' : ' use (' . implode(', ', $useFullcode) . ')') . // No space before use
                                $returnTypes .
                                $blockFullcode;

       if ($function->atom === 'Closure' &&
           $this->tokens[$current - 1][0] === $this->phptokens::T_STATIC) {
           $this->currentClassTrait->popContext();
       }

        $this->contexts->exitContext(Context::CONTEXT_CLASS);
        $this->contexts->exitContext(Context::CONTEXT_FUNCTION);
        $this->runPlugins($function, array('BLOCK' => $block));

        array_pop($this->currentFunction);
        array_pop($this->currentMethod);
        $this->currentVariables = $previousContextVariables;

        $this->pushExpression($function);

        if ($function->atom === 'Function') {
            $this->processSemicolon();
        } elseif ($function->atom === 'Closure' &&
                  $this->tokens[$current  - 1][0] !== $this->phptokens::T_EQUAL          &&
                  $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } elseif ($function->atom === 'Method' && !empty(preg_match('/^static$/i', $function->fullcode))) {
            $this->calls->addDefinition(Calls::STATICMETHOD, $function->fullnspath, $function);
            $this->currentMethods[mb_strtolower($function->code)] = $function;

            $this->sequence->ws->separators[] = '';
        } elseif ($function->atom === 'Method') {
            $this->calls->addDefinition(Calls::METHOD, $function->fullnspath, $function);
            $this->currentMethods[mb_strtolower($function->code)] = $function;
            // double call for internal reference
            $this->calls->addDefinition(Calls::STATICMETHOD, $function->fullnspath, $function);

        } elseif ($function->atom === 'Magicmethod') {
            if (mb_strtolower($this->tokens[$current + 1][1]) === '__construct' &&
                $this->currentClassTrait->getCurrent()->atom === 'Classanonymous') {
                    $this->addLink($this->currentClassTrait->getCurrent(), $function, 'DEFINITION');
            }
            $this->currentMethods[mb_strtolower($function->code)] = $function;

        }

        $function->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        return $function;
    }

    private function processOneNsname(bool $getFullnspath = self::WITH_FULLNSPATH): AtomInterface {
        $this->moveToNext();
        if ($this->nextIs(array($this->phptokens::T_NAMESPACE), 0)) {
            $this->moveToNext();
        }
        $nsname = $this->makeNsname();

        if ($getFullnspath === self::WITH_FULLNSPATH) {
            $this->getFullnspath($nsname, 'class', $nsname);
            $this->calls->addCall(Calls::A_CLASS, $nsname->fullnspath, $nsname);
        }

        return $nsname;
    }

    private function processTrait(): AtomInterface {
        $current = $this->id;
        $trait = $this->addAtom('Trait', $current);
        $this->currentClassTrait->pushContext($trait);
        $this->makePhpdoc($trait);
        $trait->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->contexts->nestContext(Context::CONTEXT_CLASS);
        $this->contexts->toggleContext(Context::CONTEXT_CLASS);

        $name = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        $this->addLink($trait, $name, 'NAME');

        $this->getFullnspath($name, 'class', $trait);
        $this->calls->addDefinition(Calls::A_CLASS, $trait->fullnspath, $trait);
        $trait->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        // Process block
        $this->makeCitBody($trait);
        $this->runPlugins($trait, array());

        $trait->fullcode   = $this->tokens[$current][1] . ' ' . $name->fullcode . static::FULLCODE_BLOCK;
        $trait->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->addToSequence($trait);
        $this->sequence->ws->separators[] = '';

        $this->contexts->exitContext(Context::CONTEXT_CLASS);

        $this->currentClassTrait->popContext();

        return $trait;
    }

    private function processInterface(): AtomInterface {
        $current = $this->id;
        $interface = $this->addAtom('Interface', $current);
        $this->currentClassTrait->pushContext($interface);
        $this->makePhpdoc($interface);
        $extras = array('EXTENDS'   => array(),
                        );
        $interface->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->contexts->nestContext(Context::CONTEXT_CLASS);
        $this->contexts->toggleContext(Context::CONTEXT_CLASS);

        $name = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        $this->addLink($interface, $name, 'NAME');

        $this->getFullnspath($name, 'class', $interface);

        $this->calls->addDefinition(Calls::A_CLASS, $interface->fullnspath, $interface);

        $this->checkPhpdoc();

        // Process extends
        $rank = 0;
        $fullcode= array();
        $extendsKeyword = '';
        if ($this->nextIs(array($this->phptokens::T_EXTENDS))) {
            $extendsKeyword = $this->tokens[$this->id + 1][1];
            $interface->ws->toextends =  $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            do {
                $this->moveToNext(); // Skip extends or ,
                $this->checkPhpdoc();
                $extends = $this->processOneNsname(self::WITH_FULLNSPATH);
                $extends->rank = $rank;

                $this->addLink($interface, $extends, 'EXTENDS');
                $this->calls->addCall(Calls::A_CLASS, $extends->fullnspath, $extends);

                $fullcode[] = $extends->fullcode;
                $extras['EXTENDS'][] = $extends;
                $interface->ws->toextendsseparator[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            } while ($this->nextIs(array($this->phptokens::T_COMMA)));
            array_pop($interface->ws->toextendsseparator);
            $interface->ws->toextendsseparator[] = '';
        }

        $this->checkPhpdoc();
        $interface->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        // Process block
        $this->makeCitBody($interface);

        $this->runPlugins($interface, $extras);

        $interface->fullcode   = $this->tokens[$current][1] . ' ' . $name->fullcode . (empty($extendsKeyword) ? '' : ' ' . $extendsKeyword . ' ' . implode(', ', $fullcode)) . static::FULLCODE_BLOCK;
        $interface->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->addToSequence($interface);
        $this->sequence->ws->separators[] = '';

        $this->contexts->exitContext(Context::CONTEXT_CLASS);
        $this->currentClassTrait->popContext();

        return $interface;
    }

    private function makeCitBody(AtomInterface $class): void {
        $this->moveToNext();
        $rank = -1;

        $this->currentProperties      = array();
        $this->currentPropertiesCalls = array();
        $this->currentMethods         = array();
        $this->currentMethodsCalls    = array();

        $this->checkPhpdoc();
        while(!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
            $this->checkAttribute();
            $this->checkPhpdoc();
            $cpm = $this->processNext();
            $class->ws->bodyseparator[] = '';
            $this->popExpression();

            switch ($cpm->atom) {
                case 'Usetrait':
                    $link = 'USE';
                    break;

                case 'Phpdoc':
                    // Skip everything for phpdocs
                    continue 2;

                default:
                    $link = strtoupper($cpm->atom);
                    break;
            }
            $cpm->rank = ++$rank;

            if ($class->atom === 'Interface' && $cpm->isA(array('Method', 'Magicmethod'))) {
                $cpm->abstract = self::ABSTRACT;
            }

            $this->addLink($class, $cpm, $link);
            $last = count($class->ws->bodyseparator) - 1;
            if ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {
                $this->moveToNext();
                $class->ws->bodyseparator[$last] .= ';' . $this->tokens[$this->id][4];
            } else {
                $class->ws->bodyseparator[$last] = '';
            }
            $this->checkPhpdoc();
        }

        $currentClass = $this->currentClassTrait->getCurrent();

        $diff = array_diff(array_keys($this->currentPropertiesCalls), array_keys($this->currentProperties));
        foreach($diff as $missing) {
            $ppp = $this->addAtom('Ppp');
            $ppp->fullcode     = 'public $' . $missing;
            $ppp->visibility   = 'none';
            $ppp->code         = $missing;
            $ppp->count        = 1;
            $ppp->line         = -1;
            $this->addLink($currentClass, $ppp, 'PPP');

            $virtual = $this->addAtom('Virtualproperty');
            $virtual->fullcode     = '$' . $missing;
            $virtual->propertyname = $missing;
            $virtual->line         = -1;
            $this->addLink($ppp, $virtual, 'PPP');
            $this->addLink($virtual, $this->addAtomVoid(), 'DEFAULT');

            foreach($this->currentPropertiesCalls[$missing] as $member) {
                $this->addLink($virtual, $member, 'DEFINITION');
            }

            $this->currentProperties[$missing] = $virtual;
        }

        $diff = array_diff(array_keys($this->currentMethodsCalls), array_keys($this->currentMethods));
        foreach($diff as $missing) {
            $virtual = $this->addAtom('Virtualmethod');
            $virtual->fullcode     = 'function ' . $missing . ' ( ) { /**/ } ';
            $virtual->visibility   = 'none';
            $virtual->code         = mb_strtolower($missing);
            $virtual->line         = -1;
            $this->addLink($currentClass, $virtual, 'METHOD');
            // TODO : may be MAGICMETHOD ?

            foreach($this->currentMethodsCalls[$missing] as $member) {
                $this->addLink($virtual, $member, 'DEFINITION');
            }

            $this->currentMethods[$missing] = $virtual;
        }

        $this->currentProperties      = array();
        $this->currentPropertiesCalls = array();
        $this->currentMethods         = array();
        $this->currentMethodsCalls    = array();

        $this->moveToNext();
    }

    private function processEnumCase(): AtomInterface {
        $case = $this->addAtom('Enumcase', $this->id);

        $name = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        $case->fullcode = 'case ' . $name->fullcode;
        $this->addLink($case, $name, 'NAME');
        $this->moveToNext();

        $this->calls->addDefinition(Calls::STATICCONSTANT,   $this->currentClassTrait->getCurrent()->fullnspath . '::' . $name->fullcode, $case);

        if ($this->nextIs(array($this->phptokens::T_EQUAL), 0)) {
            $default = $this->processNext();
            $this->addLink($case, $default, 'DEFAULT');
            $case->fullcode .= ' = ' . $default->fullcode;
        }

        return $case;
    }

    private function processImplements(AtomInterface $class): string {
        // Process implements
        if (!$this->nextIs(array($this->phptokens::T_IMPLEMENTS))) {
            $this->checkPhpdoc();
            return '';
        }

        $class->ws->toimplements =  $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $implementsKeyword = $this->tokens[$this->id + 1][1];
        $fullcodeImplements = array();
        $extras = array('IMPLEMENTS' => array());
        do {
            $this->moveToNext(); // Skip implements
            $this->checkPhpdoc();
            $implements = $this->processOneNsname(self::WITHOUT_FULLNSPATH);
            $class->ws->toimplementsseparator[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

            $this->addLink($class, $implements, 'IMPLEMENTS');
            $fullcodeImplements[] = $implements->fullcode;
            $extras['IMPLEMENTS'][] = $implements;

            $this->getFullnspath($implements, 'class', $implements);
            $this->calls->addCall(Calls::A_CLASS, $implements->fullnspath, $implements);

        } while ($this->nextIs(array($this->phptokens::T_COMMA)));
        array_pop($class->ws->toimplementsseparator);
        $class->ws->toimplementsseparator[] = '';
        $implements = (empty($implements) ? '' : ' ' . $implementsKeyword . ' ' . implode(', ', $fullcodeImplements));
        $this->runPlugins($class, $extras);

        $this->checkPhpdoc();

        return $implements;
    }

    private function processEnum(): AtomInterface {
        $current = $this->id;
        $enum = $this->addAtom('Enum', $current);

        $name = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);

        $this->getFullnspath($name, 'class', $enum);

        $this->calls->addDefinition(Calls::A_CLASS, $enum->fullnspath, $enum);
        $this->addLink($enum, $name, 'NAME');

        if ($this->nextIs(array($this->phptokens::T_COLON))) {
            $this->moveToNext();
            $returnTypes = ' : ' . $this->processTypehint($enum);
        } else {
            $returnTypes = '';
        }

        $implements = $this->processImplements($enum);

        $this->currentClassTrait->pushContext($enum);

        $this->makePhpdoc($enum);

        $enum->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->contexts->nestContext(Context::CONTEXT_CLASS);
        $this->contexts->toggleContext(Context::CONTEXT_CLASS);
        $this->contexts->nestContext(Context::CONTEXT_NEW);
        $this->contexts->nestContext(Context::CONTEXT_FUNCTION);

        $previousContextVariables = $this->currentVariables;
        $this->currentVariables = new ContextVariables();

        $extras = array();

        // Process block
        $this->makeCitBody($enum);

        $this->contexts->exitContext(Context::CONTEXT_CLASS);
        $this->contexts->exitContext(Context::CONTEXT_NEW);
        $this->contexts->exitContext(Context::CONTEXT_FUNCTION);

        $this->currentClassTrait->popContext();

        $this->currentVariables = $previousContextVariables;

        $this->runPlugins($enum, $extras);

        $enum->fullcode   = $this->tokens[$current][1] . ' ' . $name->fullcode
                            . $returnTypes
                            . $implements
                            . static::FULLCODE_BLOCK;

        $this->addToSequence($enum);
        $this->sequence->ws->separators[] = '';

        return $enum;
    }

    private function processClass(): AtomInterface {
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_STRING))) {
            $class = $this->addAtom('Class', $current);

            $name = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);

            $this->getFullnspath($name, 'class', $class);

            $this->calls->addDefinition(Calls::A_CLASS, $class->fullnspath, $class);
            $this->addLink($class, $name, 'NAME');
        } else {
            if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
                // Process arguments
                $this->moveToNext(); // Skip (
                $argsId = $this->id;
                $class = $this->processArguments('Classanonymous', array());
                $class->ws->toargs = $this->tokens[$argsId][1] . $this->tokens[$argsId][4];
                $class->ws->endargs = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                $argumentsFullcode = $class->fullcode;
            } else {
                $class = $this->addAtom('Classanonymous', $current);
            }

            $class->fullnspath = $this->anonymousNames->getName(AnonymousNames::A_CLASS);
            $this->calls->addDefinition(Calls::A_CLASS, $class->fullnspath, $class);
        }
        $this->makePhpdoc($class);
        $attributes = $this->makeAttributes($class);

        $class->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->currentClassTrait->pushContext($class);

        $this->contexts->nestContext(Context::CONTEXT_CLASS);
        $this->contexts->toggleContext(Context::CONTEXT_CLASS);
        $this->contexts->nestContext(Context::CONTEXT_NEW);
        $this->contexts->nestContext(Context::CONTEXT_FUNCTION);

        $previousContextVariables = $this->currentVariables;
        $this->currentVariables = new ContextVariables();

        $extras = array('ATTRIBUTE' => $attributes,
                        );
        // Process extends
        if ($this->nextIs(array($this->phptokens::T_EXTENDS))) {
            $extendsKeyword = $this->tokens[$this->id + 1][1];
            $this->moveToNext(); // Skip extends
            $class->ws->toextends =  $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->checkPhpdoc();
            $extends = $this->processOneNsname(self::WITHOUT_FULLNSPATH);
            $class->ws->toextendsseparator[] = '';

            $this->addLink($class, $extends, 'EXTENDS');
            $this->getFullnspath($extends, 'class', $extends);
            $extras['EXTENDS'] = $extends;

            $this->calls->addCall(Calls::A_CLASS, $extends->fullnspath, $extends);
        } else {
            $extends = '';
            $class->ws->toextends = '';
        }
        $this->checkPhpdoc();

        $implements = $this->processImplements($class);

        $class->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        // Process block
        $this->makeCitBody($class);

        $class->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->runPlugins($class, $extras);

        $class->fullcode   = $this->tokens[$current][1] . ($class->atom === 'Classanonymous' ? '' : ' ' . $name->fullcode)
                             . (isset($argumentsFullcode) ? ' (' . $argumentsFullcode . ')' : '')
                             . (empty($extends) ? '' : ' ' . $extendsKeyword . ' ' . $extends->fullcode)
                             . $implements
                             . static::FULLCODE_BLOCK;

        // Case of anonymous classes
        if ($this->tokens[$current - 1][0] === $this->phptokens::T_NEW) {
            $this->pushExpression($class);
        } else {
            $this->addToSequence($class);
            $this->sequence->ws->separators[] = '';
        }

        $this->contexts->exitContext(Context::CONTEXT_CLASS);
        $this->contexts->exitContext(Context::CONTEXT_NEW);
        $this->contexts->exitContext(Context::CONTEXT_FUNCTION);

        $this->currentClassTrait->popContext();

        $this->currentVariables = $previousContextVariables;
        return $class;
    }

    private function processOpenTag(): AtomInterface {
        $current = $this->id;
        $phpcode = $this->addAtom('Php', $current);
        $phpcode->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->startSequence();

        $this->checkPhpdoc();

        // Special case for pretty much empty script (<?php .... END)
        if ($this->nextIs(array($this->phptokens::T_END))) {
            $void = $this->addAtomVoid();
            $void->ws->closing = '';
            $this->addToSequence($void);

            $this->addLink($phpcode, $this->sequence, 'CODE');
            $this->sequence->ws->opening = '';
            $this->sequence->ws->separators[] = '';
            $this->endSequence();
            $closing = '';

            $phpcode->ws->closing = '';
            $phpcode->code        = $this->tokens[$current][1];
            $phpcode->close_tag   = self::NO_CLOSING_TAG;

            return $phpcode;
        }

        $n = count($this->tokens) - 2;
        if ($this->tokens[$n][0] === $this->phptokens::T_INLINE_HTML) {
            --$n;
        }

        while ($this->id < $n) {
            if ($this->nextIs(array($this->phptokens::T_OPEN_TAG_WITH_ECHO), 0)) {
                --$this->id;
                $echo = $this->processOpenWithEcho();
                /// processing the first expression as an echo
                $this->addToSequence($echo);
                if ($this->nextIs(array($this->phptokens::T_END))) {
                    --$this->id;
                }
            } elseif ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), 0)) {
                --$this->id;
            }
            $this->processNext();
        }

        if ($this->nextIs(array($this->phptokens::T_INLINE_HTML), 0)) {
            --$this->id;
        }

        if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), -1)) {
            $closeTag = self::CLOSING_TAG;
            $closing = $this->tokens[$this->id - 1][1]; // This includes final new lines
        } elseif ($this->nextIs(array($this->phptokens::T_HALT_COMPILER), 0)) {
            $closeTag = self::NO_CLOSING_TAG;
            $this->moveToNext(); // Go to HaltCompiler
            $this->processHalt();
            $closing = '';
        } else {
            // Why?
            // This prevents a missing separator, on the main php code sequence, with some < ?php if ( true ) {	? >A<?php }
            $this->sequence->ws->separators[] = '';

            $closeTag = self::NO_CLOSING_TAG;
            $closing = '';
        }

        $phpcode->ws->closing  = $closing;
        if ($this->nextIs(array($this->phptokens::T_OPEN_TAG), -1)) {
            $void = $this->addAtomVoid();
            $this->addToSequence($void);
        }

        $this->sequence->ws->opening = '';
        $this->addLink($phpcode, $this->sequence, 'CODE');
        $this->endSequence();

        $phpcode->code         = $this->tokens[$current][1];
        $phpcode->fullcode     = '<?php ' . self::FULLCODE_SEQUENCE . ' ' . $closing;
        $phpcode->token        = $this->getToken($this->tokens[$current][0]);
        $phpcode->close_tag    = $closeTag;

        return $phpcode;
    }

    private function processSemicolon(): AtomInterface {
        $atom = $this->popExpression();
        if ($atom->atom === 'Void') {
            $atom->ws->closing = '';
        }
        if ($this->tokens[$this->id][1] === ';') {
            $this->sequence->ws->separators[] = ';' . $this->tokens[$this->id][4];
        } else {
            $this->sequence->ws->separators[] = '';
        }
        $this->addToSequence($atom);

        return $atom;
    }

    private function processClosingTag(): AtomInterface {
        if ($this->nextIs(array($this->phptokens::T_INLINE_HTML)) &&
            $this->nextIs(array($this->phptokens::T_OPEN_TAG,
                                $this->phptokens::T_OPEN_TAG_WITH_ECHO,
                                $this->phptokens::T_INLINE_HTML), 2)) {

            // it is possible to have multiple INLINE_HTML in a row : <?php//b ? >
            do {
                $this->moveToNext();
                assert($this->nextIs(array($this->phptokens::T_INLINE_HTML), 0), 'Not an inline HTML : ' . print_r($this->tokens[$this->id], true));
                $return = $this->processInlinehtml();
                $this->addToSequence($return);
                $return->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            } while( $this->nextIs(array($this->phptokens::T_INLINE_HTML)));

            if ($this->nextIs(array($this->phptokens::T_OPEN_TAG_WITH_ECHO))) {
                $return = $this->processOpenWithEcho();

                if ($this->nextIs(array($this->phptokens::T_SEMICOLON), 0)) {
                    $this->addToSequence($return);

                    // for ; ending the <?= 'c';
                    $this->sequence->ws->separators[] = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                } elseif (!$this->nextIs(array($this->phptokens::T_SEMICOLON))) {
                    $this->addToSequence($return);
                }
            } else {
                $this->sequence->ws->separators[] = '';
                $this->moveToNext(); // set to opening tag
            }

        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_TAG,
                                      $this->phptokens::T_OPEN_TAG_WITH_ECHO,
                                      ))) {

            if ($this->nextIs(array($this->phptokens::T_OPEN_TAG_WITH_ECHO))) {
                $return = $this->processOpenWithEcho();
                if (!$this->nextIs(array($this->phptokens::T_SEMICOLON))) {
                    $this->addToSequence($return);
                    $this->sequence->ws->separators[] = '?><?= /*Y*/ ';
                }
                $return->ws->opening = '';
            } else {
                $return = $this->addAtomVoid();
                $this->addToSequence($return);
                $this->sequence->ws->separators[] = '?><?php /*Z*/ ';
                $this->moveToNext(); // set to opening tag
            }

        } else {
            // This generates unused Void Atoms, but is required to return a value.
            $this->moveToNext();
            $return = $this->addAtomVoid();
        }

        return $return;
    }

    private function processOpenWithEcho(): AtomInterface {
        // Processing ECHO
        $echo = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);

        $noSequence = $this->contexts->isContext(Context::CONTEXT_NOSEQUENCE);
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }
        $functioncall = $this->processArguments('Echo',
                                                array($this->phptokens::T_SEMICOLON,
                                                      $this->phptokens::T_CLOSE_TAG,
                                                      $this->phptokens::T_END,
                                                      ));
        $argumentsFullcode = $functioncall->fullcode;

        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }

        //processArguments goes too far, up to ;
        if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), 0)) {
            --$this->id;
        }

        $functioncall->code        = $echo->code;
        $functioncall->fullcode    = '<?= ' . $argumentsFullcode;
        $functioncall->token       = 'T_OPEN_TAG_WITH_ECHO';
        $functioncall->fullnspath  = '\echo';
        $functioncall->ws->opening = '';
        $functioncall->ws->closing = $this->tokens[$this->id + 1][4];

        $this->addLink($functioncall, $echo, 'NAME');

        return $functioncall;
    }

    private function makeNsname(): AtomInterface {
        if ($this->nextIs(array($this->phptokens::T_NAME_QUALIFIED), 0)) {
            $fullcode = array($this->tokens[$this->id][1]);
            $token = 'T_NAME_QUALIFIED';
            $absolute = self::NOT_ABSOLUTE;

            if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
                $atom = 'Newcallname';
            } else {
                $atom = 'Nsname';
            }
        } elseif ($this->nextIs(array($this->phptokens::T_NAME_FULLY_QUALIFIED), 0)) {
            $fullcode = array($this->tokens[$this->id][1]);
            $token = 'T_NAME_FULLY_QUALIFIED';
            $absolute = self::ABSOLUTE;

            if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
                $atom = 'Newcallname';
            } elseif (in_array(mb_strtolower($this->tokens[$this->id][1]), array('\\true', '\\false'), \STRICT_COMPARISON)) {
                $atom = 'Boolean';
            } elseif (in_array(mb_strtolower($this->tokens[$this->id][1]), array('\\null'), \STRICT_COMPARISON)) {
                $atom = 'Null';
            } else {
                $atom = 'Nsname';
            }
        } elseif ($this->nextIs(array($this->phptokens::T_NAME_RELATIVE), 0)) {
            $fullcode = array($this->tokens[$this->id][1]);
            $token = 'T_NAME_RELATIVE';
            $absolute = self::NOT_ABSOLUTE;

            if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
                $atom = 'Newcallname';
            } else {
                $atom = 'Nsname';
            }
        } else {
            $token = 'T_NS_SEPARATOR';

            if ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 0)                   &&
                $this->nextIs(array($this->phptokens::T_STRING))                                       &&
                in_array(mb_strtolower($this->tokens[$this->id + 1][1]), array('true', 'false'), \STRICT_COMPARISON) &&
                !$this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 2)
                ) {
                $atom = 'Boolean';

            } elseif ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 0) &&
                      $this->nextIs(array($this->phptokens::T_STRING))                     &&
                      mb_strtolower($this->tokens[$this->id + 1][1]) === 'null'            &&
                      !$this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 2)           ) {

                $atom = 'Null';
            } elseif (mb_strtolower($this->tokens[$this->id][1]) === 'parent') {
                $atom = 'Parent';
            } elseif (mb_strtolower($this->tokens[$this->id][1]) === 'self') {
                $atom = 'Self';
            } elseif ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 0) &&
                      $this->nextIs(array($this->phptokens::T_STRING))                     &&
                      mb_strtolower($this->tokens[$this->id + 1][1]) === 'self'            &&
                      !$this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 2)           ) {

                $atom = 'Self';
            } elseif ($this->contexts->isContext(Context::CONTEXT_NEW)) {
                $atom = 'Newcall';
            } else {
                $atom = 'Nsname';
                $token = 'T_STRING';
            }

            $fullcode = array();

            if ($this->nextIs(array($this->phptokens::T_STRING), 0)) {
                $fullcode[] = $this->tokens[$this->id][1];
                $this->moveToNext();

                $absolute = self::NOT_ABSOLUTE;
            } elseif ($this->nextIs(array($this->phptokens::T_NAMESPACE), -1)) {
                $fullcode[] = $this->tokens[$this->id - 1][1];

                $absolute = self::NOT_ABSOLUTE;
            } elseif ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 0)) {
                $fullcode[] = '';

                $absolute = self::ABSOLUTE;
            } else {
                $fullcode[] = $this->tokens[$this->id][1];
                $this->moveToNext();

                $absolute = self::NOT_ABSOLUTE;
            }

            while ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 0)    &&
                   !$this->nextIs(array($this->phptokens::T_OPEN_CURLY))
                   ) {
                $this->moveToNext(); // skip \
                $fullcode[] = $this->tokens[$this->id][1];

                // Go to next
                $this->moveToNext(); // skip \
                $token = 'T_NS_SEPARATOR';
            }

            // Back up a bit
            --$this->id;
        }

        if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
            if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
                $atom = 'Newcallname';
            } elseif ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON), 0)) {
                // Finally, it is D::$D
                $atom = 'Identifier';
            }
        }

        $nsname = $this->addAtom($atom);
        $nsname->code        = implode('\\', $fullcode);
        $nsname->fullcode    = $nsname->code;
        $nsname->token       = $token;
        $nsname->absolute    = $absolute;
        $nsname->ws->opening = $nsname->code;
        $this->runPlugins($nsname);

        return $nsname;
    }

    private function processNsname(): AtomInterface {
        $nsname = $this->makeNsname();

        // Review this : most nsname will end up as constants!
        if ($this->nextIs(array($this->phptokens::T_INSTANCEOF), -1)   ||
            $this->nextIs(array($this->phptokens::T_DOUBLE_COLON))     ||
            $this->nextIs(array($this->phptokens::T_VARIABLE       ))) {

            $this->getFullnspath($nsname, 'class', $nsname);

            $this->calls->addCall(Calls::A_CLASS, $nsname->fullnspath, $nsname);

        } elseif ($this->contexts->isContext(Context::CONTEXT_NEW) &&
                  !$this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            $this->getFullnspath($nsname, 'class', $nsname);
            $this->calls->addCall(Calls::A_CLASS, $nsname->fullnspath, $nsname);

        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            // DO nothing

        } else {
            $this->calls->addCall(Calls::CONST, $nsname->fullnspath, $nsname);
        }

        $this->pushExpression($nsname);

        return $this->processFCOA($nsname);
    }

    private function processTypehint(AtomInterface $holder): string {
        $typehintToken = array($this->phptokens::T_NS_SEPARATOR,
                               $this->phptokens::T_STRING,
                               $this->phptokens::T_NAMESPACE,
                               $this->phptokens::T_ARRAY,
                               $this->phptokens::T_CALLABLE,
                               $this->phptokens::T_STATIC,
                               $this->phptokens::T_QUESTION,
                               $this->phptokens::T_NAME_QUALIFIED,
                               $this->phptokens::T_NAME_RELATIVE,
                               $this->phptokens::T_NAME_FULLY_QUALIFIED,
        );

        // default typehint style is 'one'
        $holder->typehint = 'one';

        // return type allows for static. Not valid for arguments.
        if ($holder->isA(array('Ppp', 'Parameter', 'Enum'))) {
            $link = 'TYPEHINT';
            $holder->ws->totype = '';
        } else {
            $link = 'RETURNTYPE';
            $holder->ws->totype = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

            // Skip : if it is here (it might not)
            if ($this->nextIs(array($this->phptokens::T_COLON))) {
                $this->moveToNext();
            }
        }

        if (!$this->nextIs($typehintToken)) {
            if ($this->nextIs(array($this->phptokens::T_ELLIPSIS))) {
                $typehint = $this->addAtom('Scalartypehint', $this->id + 1);
                $typehint->fullnspath = '\\array';
                $typehint->fullcode = '';
            } else {
                $typehint = $this->addAtomVoid();
                $typehint->rank = 0;
            }

            $this->addLink($holder, $typehint, $link);
            $holder->ws->totype = '';

            return '';
        }

        $return = array();

        if ($this->nextIs(array($this->phptokens::T_QUESTION))) {
            $null = $this->addAtom('Scalartypehint');
            $null->code        = '?';
            $null->fullcode    = '?';
            $null->token       = $this->phptokens::T_STRING;
            $null->noDelimiter = '';
            $null->delimiter   = '';
            $null->fullnspath  = '\\null';
            $null->ws->closing = $this->tokens[$this->id + 1][4];

            $return[] = $null;
            $this->moveToNext();

            $holder->typehint = 'or';
        }

        --$this->id;
        do {
            $this->moveToNext();
            if (in_array(mb_strtolower($this->tokens[$this->id + 1][1]), $this->SCALAR_TYPE, \STRICT_COMPARISON) &&
                 !$this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 2)) {
                $this->moveToNext();
                $nsname = $this->processSingle('Scalartypehint');
                $nsname->fullnspath = '\\' . mb_strtolower($nsname->code);
            } elseif ($this->nextIs(array($this->phptokens::T_STATIC))) {
                $this->moveToNext();

                $nsname = $this->addAtom('Static');
                $nsname->code        = $this->tokens[$this->id][1];
                $nsname->fullcode    = $this->tokens[$this->id][1];
                $nsname->token       = $this->phptokens::T_STATIC;
                $nsname->noDelimiter = '';
                $nsname->delimiter   = '';
                $nsname->fullnspath  = '\\static';
                $nsname->ws->closing = $this->tokens[$this->id + 1][4];

            } elseif (mb_strtolower($this->tokens[$this->id + 1][1]) === 'null') {
                $this->moveToNext();
                $nsname = $this->processSingle('Null');
                $nsname->fullnspath = '\\null';
            } else {
                $nsname = $this->processOneNsname(self::WITHOUT_FULLNSPATH);
                $this->getFullnspath($nsname, 'class', $nsname);
                $this->calls->addCall(Calls::A_CLASS, $nsname->fullnspath, $nsname);
            }

            if ($this->nextIs(array($this->phptokens::T_OR))) {
                $holder->typehint = 'or';
                $nsname->ws->closing .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            }

            if ($this->nextIs(array($this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG))) {
                $holder->typehint = 'and';
                $nsname->ws->closing .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            }

            $return[] = $nsname;
        } while ($this->nextIs(array($this->phptokens::T_OR,
                                     $this->phptokens::T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG))
                                     &&
                 !$this->nextIs(array($this->phptokens::T_VARIABLE), 2)
                );

        if ($this->tokens[$this->id + 1][1] === ',') {
            $this->moveToNext();
        }

        $this->runPlugins($holder, array($link => $return));

        if ($return[0]->code === '?') {
            $this->addLink($holder, $return[0], $link);
            $this->addLink($holder, $return[1], $link);

            $return[0]->rank = 0;
            $return[1]->rank = 1;

            $returnTypeFullcode = '?' . $return[1]->fullcode;
        } else {
            $fullcode = array();
            $rank = -1;
            foreach($return as $returnType) {
                $this->addLink($holder, $returnType, $link);
                $returnType->rank = ++$rank;

                if (!$returnType->isA(array('Void'))) {
                    $fullcode[] = $returnType->fullcode;
                } elseif ($returnType->code === '?') {
                    array_unshift($fullcode, '?');
                    $fullcode = array_values($fullcode);
                }
            }

            if ($holder->typehint === 'or') {
                $returnTypeFullcode = implode('|', $fullcode);
            } else {
                $returnTypeFullcode = implode('&', $fullcode);
            }
        }

        switch($link) {
            case 'RETURNTYPE':
                $returnTypeFullcode = ' : ' . $returnTypeFullcode;
                break;

            case 'TYPEHINT':
                $returnTypeFullcode .= ' ';
                break;

            default:
                assert(false, 'Typehint link is neither RETURNTYPE nor TYPEHINT.');
        }

        return $returnTypeFullcode;
    }

    private function processParameters(string $atom): AtomInterface {
        $current   = $this->id;
        $arguments = $this->addAtom($atom, $current);
        $this->makePhpdoc($arguments);
        $this->makeAttributes($arguments);

        $this->currentFunction[] = $arguments;
        $this->currentMethod[]   = $arguments;

        $argumentsList  = array();

        $this->checkAttribute();
        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $void = $this->addAtomVoid();
            $void->rank = 0;
            $this->addLink($arguments, $void, 'ARGUMENT');
            $void->ws->closing = '';

            $arguments->code     = $this->tokens[$current][1];
            $arguments->fullcode = self::FULLCODE_VOID;
            $arguments->token    = $this->getToken($this->tokens[$current][0]);
            $arguments->args_max = 0;
            $arguments->args_min = 0;
            $arguments->count    = 0;

            $this->runPlugins($arguments, array($void));

            $argumentsList[] = $void;

            // Skip the )
            $this->moveToNext();
            return $arguments;
        }

        $fullcode  = array();
        $argsMax   = 0;
        $argsMin   = 0;
        $rank      = -1;
        $default   = 0;
        $variadic  = self::NOT_ELLIPSIS;

        do {
            do {
                $this->checkPhpdoc();
                $this->checkAttribute();

                // PHP 8.0's trailing comma in signature
                if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
                    $fullcode[] = ' ';
                    $this->moveToNext();
                    break 1;
                }

                ++$argsMax;
                if ($this->nextIs(array($this->phptokens::T_PUBLIC,
                                        $this->phptokens::T_PRIVATE,
                                        $this->phptokens::T_PROTECTED,
                    ))
                ) {
                    $this->moveToNext();
                    $index = $this->processPPP(self::PROMOTED);

                    $this->moveToNext();

                    $this->addLink($this->currentClassTrait->getCurrent(), $index, 'PPP');

                    $index->rank = ++$rank;
                    $this->popExpression();
                    $fullcode[] = $index->fullcode;
                    $this->addLink($arguments, $index, 'ARGUMENT');
                    $argumentsList[] = $index;

                    continue;
                }

                $index = $this->addAtom('Parameter');

                $typehints = $this->processTypehint($index);
                $this->checkPhpdoc();
                $this->moveToNext();

                if ($this->nextIs(array($this->phptokens::T_AND,
                                        $this->phptokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG), 0)) {
                    $reference = self::REFERENCE;
                    $referenceWS = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                    $this->moveToNext();
                } else {
                    $reference   = self::NOT_REFERENCE;
                    $referenceWS = null;
                }

                if ($this->nextIs(array($this->phptokens::T_ELLIPSIS), 0)) {
                    $variadic = self::ELLIPSIS;
                    $ellipsisWS = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                    $this->moveToNext();
                }

                assert($this->nextIs(array($this->phptokens::T_VARIABLE), 0), 'No variable in parameter list (' . $this->tokens[$this->id][1] . ') in file ' . $this->filename . ' on line ' . $this->tokens[$this->id][2]);

                $variable   = $this->addAtom('Parametername');
                $attributes = $this->makeAttributes($index);
                $this->makePhpdoc($index);

                $variable->code     = $this->tokens[$this->id][1];
                $variable->fullcode = $this->tokens[$this->id][1];
                $variable->token    = $this->getToken($this->tokens[$this->id][0]);
                $variable->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                $variable->ws->closing = '';
                $this->runPlugins($variable);

                $index->code     = $variable->fullcode;
                $index->fullcode = $variable->fullcode;
                $index->token    = 'T_VARIABLE';

                if ($variadic === self::ELLIPSIS) {
                    $index->fullcode     = '...' . $index->fullcode;
                    $index->variadic     = self::ELLIPSIS;
                    $index->ws->ellipsis = $ellipsisWS;
                }

                if ($reference === self::REFERENCE) {
                    $index->fullcode  = '&' . $index->fullcode;
                    $index->reference = self::REFERENCE;
                    $index->ws->reference = $referenceWS;
                }

                $this->addLink($index, $variable, 'NAME');
                $variable->fullnspath = $index->fullnspath;
                $variable->isPhp      = $index->isPhp;
                $variable->isExt      = $index->isExt;
                $variable->isStub     = $index->isStub;
                $this->currentVariables->set($variable->code, $variable);

                $this->checkPhpdoc();
                if ($this->nextIs(array($this->phptokens::T_EQUAL))) {
                    $this->moveToNext(); // Skip =
                    $index->ws->operator = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                    $finals      = array($this->phptokens::T_COMMA,
                                         $this->phptokens::T_CLOSE_PARENTHESIS
                                         );
                    $default = $this->processExpression($finals);
                } else {
                    if ($index->variadic === self::ELLIPSIS) {
                        $argsMax = \MAX_ARGS;
                    } else {
                        ++$argsMin;
                    }
                    $default = $this->addAtomVoid();
                }
                $this->addLink($index, $default, 'DEFAULT');
                if ($default->atom !== 'Void') {
                    $index->fullcode .= ' = ' . $default->fullcode;

                    // When Null is default, then typehint is also nullable
                    if ($default->atom === 'Null' &&
                        strpos($typehints, '?') === false &&
                        preg_match('/\bnull\b/i', $typehints) === 0
                        ) {
                        $this->addLink($index, $default, 'TYPEHINT');
                    }
                }

                $index->rank = ++$rank;

                $index->fullcode = $typehints . $index->fullcode;
                $fullcode[] = $index->fullcode;
                $this->addLink($arguments, $index, 'ARGUMENT');
                $this->runPlugins($index, array('ATTRIBUTE' => $attributes));
                $argumentsList[] = $index;

                $this->moveToNext();
                $arguments->ws->separators[] = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            } while ($this->nextIs(array($this->phptokens::T_COMMA), 0));

            --$this->id;
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS)));
        $arguments->count    = $rank + 1;
        array_pop($arguments->ws->separators);

        // Skip the )
        $this->moveToNext();

        $arguments->fullcode = implode(', ', $fullcode);
        $arguments->token    = 'T_COMMA';
        $arguments->args_max = $argsMax;
        $arguments->args_min = $argsMin;
        $this->runPlugins($arguments, $argumentsList);

        return $arguments;
    }

    private function processArguments(string $atom, array $finals = array(), array &$argumentsList = array()): AtomInterface {
        if (empty($finals)) {
            $finals = array($this->phptokens::T_CLOSE_PARENTHESIS);
        }
        $current = $this->id;
        $arguments = $this->addAtom($atom, $current);
        $this->makePhpdoc($arguments);
        $this->makeAttributes($arguments);

        $argumentsId = array();

        $this->contexts->nestContext(Context::CONTEXT_NEW);
        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        $fullcode = array();

        // case of empty arguments : adding void
        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS,
                                $this->phptokens::T_CLOSE_BRACKET))) {
            $void = $this->addAtomVoid();
            $void->rank = 0;
            $void->ws->closing = '';
            $this->addLink($arguments, $void, 'ARGUMENT');
            $arguments->ws->separators[] = '';

            $arguments->code     = $this->tokens[$current][1];
            $arguments->fullcode = self::FULLCODE_VOID;
            $arguments->token    = $this->getToken($this->tokens[$current][0]);
            $arguments->args_max = 0;
            $arguments->args_min = 0;
            $arguments->count    = 0;
            $argumentsId[]       = $void;
            // $argument->ws is default value

            $argumentsList = array($void);
            $this->runPlugins($arguments, $argumentsList);

            $this->moveToNext();

            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
            $this->contexts->exitContext(Context::CONTEXT_NEW);

            return $arguments;
        }

        // case strlen(...)
        if ( $this->nextIs(array($this->phptokens::T_ELLIPSIS)) &&
             $this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS,
                                 $this->phptokens::T_CLOSE_BRACKET), 2)) {
            $void = $this->addAtomVoid();
            $void->rank = 0;
            $void->ws->closing = '';
            $void->fullcode = '...';
            $void->variadic = self::VARIADIC;
            $void->token    = 'T_ELLIPSIS';
            $this->addLink($arguments, $void, 'ARGUMENT');
            $arguments->ws->separators[] = '';

            $arguments->code     = $this->tokens[$current][1];
            $arguments->fullcode = $this->tokens[$current + 1][1]; // the ...
            $arguments->token    = $this->getToken($this->tokens[$current][0]);
            $arguments->args_max = 0;
            $arguments->args_min = 0;
            $arguments->count    = 0;
            $argumentsId[]       = $void;
            // $argument->ws is default value

            $argumentsList = array($void);
            $this->runPlugins($arguments, $argumentsList);

            $this->moveToNext(); // skip ...
            $this->moveToNext(); // skip ...

            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
            $this->contexts->exitContext(Context::CONTEXT_NEW);

            return $arguments;
        }

        // Normal case, with some arguments
        $index      = 0;
        $argsMax    = 0;
        $argsMin    = 0;
        $rank       = -1;
        $rankName  = '';
        $argumentsList  = array();

        while (!$this->nextIs($finals)) {
            $initialId = $this->id;
            ++$argsMax;

            // named parameters PHP 8.0
            // based only on id + 2 == T_COLON
            if ($this->nextIs(array($this->phptokens::T_COLON ), 2)) {
                $this->moveToNext();
                $rankName = $this->tokens[$this->id][1];
                $this->moveToNext(); // skip :
            }

            while (!$this->nextIs(array($this->phptokens::T_COMMA,
                                        $this->phptokens::T_CLOSE_PARENTHESIS,
                                        $this->phptokens::T_CLOSE_CURLY,
                                        $this->phptokens::T_SEMICOLON,
                                        $this->phptokens::T_CLOSE_BRACKET,
                                        $this->phptokens::T_CLOSE_TAG,
                                        $this->phptokens::T_COLON,
                                        ))) {
                $index = $this->processNext();
            }
            $this->popExpression();
            if (!empty($rankName)) {
                $index->rankName = '$' . $rankName;
                $index->fullcode = $rankName . ' : ' . $index->fullcode;
            }

            while ($this->nextIs(array($this->phptokens::T_COMMA))) {
                if ($index === 0) {
                    $index = $this->addAtomVoid();
                    $index->rank = 0;
                    $index->ws->opening = '';
                    $index->ws->closing = '';
                }

                $index->rank = ++$rank;

                $this->addLink($arguments, $index, 'ARGUMENT');
                $argumentsId[] = $index;
                // array($this, 'b'); for Callback syntax.
                if ($index->atom === 'Variable' &&
                    $index->code === '$this'    &&
                    $index->rank === 0 ) {
                    $this->calls->addCall(Calls::A_CLASS, $this->currentClassTrait->getCurrent()->fullnspath, $index);
                }

                $fullcode[] = $index->fullcode;
                $arguments->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
                $argumentsList[] = $index;

                $this->moveToNext(); // Skipping the comma ,
                $index = 0;
            }

            if ($initialId === $this->id) {
                throw new NoFileToProcess($this->filename, ' not processable with the current code, on line ' . $this->tokens[$this->id][2]);
            }
        }

        if ($index === 0) {
            if ($atom === 'List') {
                $index = $this->addAtomVoid();
                $index->ws->opening = '';
                $index->ws->closing = '';

                $index->rank = ++$rank;
                $argumentsId[] = $index;
                $this->argumentsId = $argumentsId; // This avoid overwriting when nesting functioncall
                if ($this->tokens[$this->id + 1][1] === ')') {
                    $arguments->ws->separators[] = '';
                } else {
                    $arguments->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
                }

                $this->addLink($arguments, $index, 'ARGUMENT');

                $fullcode[] = $index->fullcode;
                $argumentsList[] = $index;
            } else {
                $fullcode[] = ' ';
            }
        } else {
            $index->rank = ++$rank;
            $argumentsId[] = $index;
            $this->argumentsId = $argumentsId; // This avoid overwriting when nesting functioncall
            $arguments->ws->separators[] = '';

            $this->addLink($arguments, $index, 'ARGUMENT');

            $fullcode[] = $index->fullcode;
            $argumentsList[] = $index;
        }

        // Skip the )
        $this->moveToNext();

        $arguments->fullcode = implode(', ', $fullcode);
        $arguments->token    = 'T_COMMA';
        $arguments->count    = $rank + 1;
        $arguments->args_max = $argsMax;
        $arguments->args_min = $argsMin;
        $this->runPlugins($arguments, $argumentsList);

        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->exitContext(Context::CONTEXT_NEW);

        return $arguments;
    }

    private function processNextAsIdentifier(bool $getFullnspath = self::WITH_FULLNSPATH): AtomInterface {
        $this->moveToNext();

        $identifier = $this->addAtom($getFullnspath === self::WITH_FULLNSPATH ? 'Identifier' : 'Name', $this->id);
        $identifier->fullcode    = $this->tokens[$this->id][1];
        $identifier->ws->opening = $this->tokens[$this->id][1];

        if ($getFullnspath === self::WITH_FULLNSPATH) {
            $this->getFullnspath($identifier, 'const', $identifier);
        }
        $this->runPlugins($identifier);

        return $identifier;
    }

    private function guessType(AtomInterface $atom) {
        switch($atom->atom) {
            case 'Integer' :
            case 'Addition' :
            case 'Multiplication' :
            case 'Power' :
            case 'Bitshift' :
            case 'Bitoperation':
            case 'Sign':
            case 'Spaceship':
                $type = $this->addAtom('Scalartypehint');
                $type->fullnspath = '\\int';
                $type->fullcode = 'int';
                break;

            case 'Float' :
                $type = $this->addAtom('Scalartypehint');
                $type->fullnspath = '\\float';
                $type->fullcode = 'float';
                break;

            case 'String' :
            case 'Concatenation' :
            case 'Heredoc' :
            case 'Magicconstant' :
                $type = $this->addAtom('Scalartypehint');
                $type->fullnspath = '\\string';
                $type->fullcode = 'string';
                break;

            case 'Null' :
                $type = $this->addAtom('Scalartypehint');
                $type->fullnspath = '\\null';
                $type->fullcode = 'null';
                break;

            case 'Boolean' :
            case 'Logical' :
            case 'Comparison' :
            case 'Not' :
                $type = $this->addAtom('Scalartypehint');
                $type->fullnspath = '\\bool';
                $type->fullcode = 'bool';
                break;

            case 'Arrayliteral' :
                $type = $this->addAtom('Scalartypehint');
                $type->fullnspath = '\\array';
                $type->fullcode = 'array';
                break;

            case 'New' :
                // The NEXT id is necessarly the newcallname.
                $type = $this->addAtom($this->atoms[$atom->id + 1]->token === 'T_STRING' ? 'Identifier' : 'Nsname');
                $type->fullnspath = $this->atoms[$atom->id + 1]->fullnspath;
                $type->fullcode = $this->atoms[$atom->id + 1]->fullcode;
                break;

            case 'Identifier' :
            case 'Nsname' :
            case 'Ternary' :
            case 'Coalesce' :
            case 'Staticconstant' :
            case 'Staticclass' :
            case 'Parenthesis':
            case 'Array':
                // in case of doubt, skip it.
                $type = null;
                break;

            default:
                assert(false, "No guess type for {$atom->atom}\n");
        }

        return $type;
    }

    private function processConst(): AtomInterface {
        $current = $this->id;
        $const = $this->addAtom('Const', $current);
        $const->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $this->makePhpdoc($const);
        $this->makeAttributes($const);

        $rank = -1;
        --$this->id; // back one step for the init in the next loop

        if (empty($const->visibility)) {
            $const->visibility = 'none';
            $const->ws->visibility = '';
        }

        $fullcode = array();
        do {
            $this->moveToNext();
            $this->checkPhpdoc();
            $name = $this->processNextAsIdentifier();

            $this->moveToNext(); // Skip =
            $def = $this->addAtom('Constant', $this->id);
            $def->ws->operator = '=' . $this->tokens[$this->id][4];
            $value = $this->processExpression(array($this->phptokens::T_SEMICOLON,
                                                    $this->phptokens::T_COMMA,
                                                    $this->phptokens::T_DOC_COMMENT,
                                                    ));

            $this->addLink($def, $name, 'NAME');
            $this->addLink($def, $value, 'VALUE');

            $type = $this->guessType($value);
            if (!empty($type)) {
                $this->addLink($def, $type, 'TYPEHINT');
            }

            $def->fullcode = $name->fullcode . ' = ' . $value->fullcode;
            $def->rank     = ++$rank;

            $fullcode[] = $def->fullcode;
            $this->runPlugins($def, array('VALUE' => $value,
                                          'NAME'  => $name,
                                          ));

            $this->getFullnspath($name, 'const', $name);

            $this->addLink($const, $def, 'CONST');

            if ($this->contexts->isContext(Context::CONTEXT_CLASS)) {
                $this->calls->addDefinition(Calls::STATICCONSTANT,   $this->currentClassTrait->getCurrent()->fullnspath . '::' . $name->fullcode, $def);
            } else {
                $this->calls->addDefinition(Calls::CONST, $name->fullnspath, $def);
            }
            $this->makePhpdoc($def);
            $this->checkPhpdoc();

            if ($this->nextIs(array($this->phptokens::T_COMMA))) {
                $const->ws->separators[] = ',' . $this->tokens[$this->id + 1][4];
            }
        } while (!$this->nextIs(array($this->phptokens::T_SEMICOLON)));
        $const->ws->separators[] = '';

        $const->fullcode = $this->tokens[$current][1] . ' ' . implode(', ', $fullcode);
        $const->count    = $rank + 1;

        $this->pushExpression($const);

        return $this->processFCOA($const);
    }

    private function processAbstract(): AtomInterface {
        $current = $this->id;
        $abstract = $this->tokens[$this->id][1];

        $next = $this->processNext();

        $next->abstract = self::ABSTRACT;
        $next->ws->abstract = $this->tokens[$current][1] . $this->tokens[$current][4];
        $next->fullcode = "$abstract $next->fullcode";
        $this->makePhpdoc($next);

        return $next;
    }

    private function processReadonly(bool $promoted = self::PROMOTED_NOT): AtomInterface {
        $current = $this->id;
        $readonly = $this->tokens[$this->id][1];

        if ($this->nextIs(array($this->phptokens::T_PRIVATE,
                                $this->phptokens::T_PROTECTED,
                                $this->phptokens::T_PUBLIC,
                               ))) {
            $next = $this->processNext();
            $this->makePhpdoc($next);
            $next->fullcode = "$readonly $next->fullcode";
            $next->ws->readonly = $this->tokens[$current][1] . $this->tokens[$current][4];
        } else {
            // next is variables or typehints
            $next = $this->addAtom('Ppp', $current);
            $this->makePhpdoc($next);
            $this->makeAttributes($next);
            $returnTypes = $this->processTypehint($next);

            $this->processSGVariable($next, $promoted);
            $next->ws->opening = '';

            $next->readonly = self::READONLY;
            $next->ws->readonly = $this->tokens[$current][1] . $this->tokens[$current][4];
            $next->fullcode = "$readonly $returnTypes $next->fullcode";
            $this->makePhpdoc($next);
        }

        return $next;
    }

    private function processFinal(): AtomInterface {
        $current = $this->id;
        $final = $this->tokens[$this->id][1];

        $next = $this->processNext();

        $next->final     = self::FINAL;
        $next->ws->final = $this->tokens[$current][1] . $this->tokens[$current][4];
        $next->fullcode  = "$final $next->fullcode";
        $this->makePhpdoc($next);

        return $next;
    }

    private function processVar(): AtomInterface {
        $current = $this->id;
        $visibility = $this->tokens[$this->id][1];
        $ppp = $this->addAtom('Ppp', $current);
        $returnTypes = $this->processTypehint($ppp);

        $this->processSGVariable($ppp);

        $ppp->visibility = 'none';
        $ppp->fullcode   = "$visibility {$returnTypes}$ppp->fullcode";
        $this->makePhpdoc($ppp);

        return $ppp;
    }

    private function processPPP(bool $promoted = self::PROMOTED_NOT): AtomInterface {
        $current = $this->id;
        $visibility = $this->tokens[$this->id][1];
        $this->checkPhpdoc();

        if ($this->nextIs(array($this->phptokens::T_STATIC,
                                $this->phptokens::T_FINAL,
                                $this->phptokens::T_ABSTRACT,
                                ))) {
            $ppp = $this->processNext();

            $this->makePhpdoc($ppp);
            $returnTypes = '';
        } elseif ( $this->nextIs(array($this->phptokens::T_READONLY) )) {
            $this->moveToNext();
            $ppp = $this->processReadonly($promoted);

            $this->makePhpdoc($ppp);
            $returnTypes = '';
        } elseif ($this->nextIs(array($this->phptokens::T_FUNCTION,
                                      $this->phptokens::T_CONST))) {
            $ppp = $this->processNext();
            $this->makePhpdoc($ppp);
            $returnTypes = '';
        } else {
            $ppp = $this->addAtom('Ppp', $current);
            $this->makePhpdoc($ppp);
            $this->makeAttributes($ppp);
            $returnTypes = $this->processTypehint($ppp);

            $this->processSGVariable($ppp, $promoted);
            $ppp->ws->opening = '';
        }

        $ppp->visibility = strtolower($visibility);
        $ppp->ws->visibility = $visibility . $this->tokens[$current][4];
        $ppp->fullcode   = "$visibility {$returnTypes}$ppp->fullcode";

        return $ppp;
    }

    private function processDefineConstant(AtomInterface $namecall): AtomInterface {
        $namecall->atom = 'Defineconstant';
        $namecall->fullnspath = '\\define';
        $namecall->ws->opening = $this->tokens[$this->id - 1][1] . $this->tokens[$this->id - 1][4] .
                                 $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $this->makePhpdoc($namecall);

        // Empty call
        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {

            $namecall->fullcode   = $namecall->code . '( )';
            $this->pushExpression($namecall);

            $this->runPlugins($namecall, array());
            $this->moveToNext(); // Skip )
            $namecall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->checkExpression();
            return $namecall;
        }

        // First argument : constant name
        $this->moveToNext();
        if ($this->nextIs(array($this->phptokens::T_CONSTANT_ENCAPSED_STRING), 0) &&
            $this->nextIs(array($this->phptokens::T_COMMA))
            ) {
            $name = $this->processSingle('Identifier');
            $name->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            $namecall->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

            $this->runPlugins($name);
            $name->delimiter   = $name->code[0];
            if (strtolower($name->delimiter) === 'b') {
                $name->binaryString = $name->delimiter;
                $name->delimiter    = $name->code[1];
                $name->noDelimiter  = substr($name->code, 2, -1);
            } else {
                $name->noDelimiter = substr($name->code, 1, -1);
            }
            $this->getFullnspath($name, 'const', $name);

            $this->runPlugins($name, array());
            if ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET))) {
                $name = $this->processBracket();
            }
        } else {
            // back one step
            --$this->id;
            $name = $this->processExpression(array($this->phptokens::T_COMMA,
                                                   $this->phptokens::T_CLOSE_PARENTHESIS // In case of missing arguments...
                                                   ));
            $namecall->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        }
        $this->addLink($namecall, $name, 'NAME');

        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $namecall->fullcode   = "{$namecall->code}({$name->code})";
            $this->pushExpression($namecall);

            $this->runPlugins($namecall, array('NAME'  => $name, ));
            $this->moveToNext(); // Skip )
            $namecall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->checkExpression();
            return $namecall;
        }

        // Second argument constant value
        $this->moveToNext(); // Skip ,
        $value = $this->processExpression(array($this->phptokens::T_COMMA,
                                                $this->phptokens::T_CLOSE_PARENTHESIS // In case of missing arguments...
                                               ));
        $this->addLink($namecall, $value, 'VALUE');

        // Most common point of exit
        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $namecall->fullcode   = "{$namecall->code}({$name->fullcode}, {$value->fullcode})";
            $this->pushExpression($namecall);

            $this->runPlugins($namecall, array('NAME'  => $name,
                                               'VALUE' => $value,
                                               ));
            $this->moveToNext(); // Skip )
            $namecall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->processDefineAsConstants($namecall, $name, self::CASE_INSENSITIVE);

            $this->checkExpression();

            return $namecall;
        }

        // Third argument : case sensitive
        $this->moveToNext(); // Skip ,
        $namecall->ws->separators[] = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $case = $this->processExpression(array($this->phptokens::T_COMMA,
                                               $this->phptokens::T_CLOSE_PARENTHESIS // In case of missing arguments...
                                              ));
        $this->addLink($namecall, $case, 'CASE');

        $this->processDefineAsConstants($namecall, $name, (bool) $case->boolean);

        $namecall->fullcode   = $namecall->code . '(' . $name->fullcode . ', ' . $value->fullcode . ', ' . $case->fullcode . ')';
        $this->pushExpression($namecall);

        $this->runPlugins($namecall, array('NAME'  => $name,
                                           'VALUE' => $value,
                                           'CASE'  => $case,
                                           ));

        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $this->moveToNext(); // Skip )
            $namecall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->checkExpression();
            return $namecall;
        }

        // Ignore everything else
        $parenthese = 1;
        while ($parenthese > 0) {
            $this->moveToNext();

            if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS), 0)) {
                --$parenthese;
            } elseif ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS), 0)) {
                ++$parenthese;
            }
        }

        $namecall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $this->checkExpression();
        return $namecall;
    }

    private function processFunctioncall(bool $getFullnspath = self::WITH_FULLNSPATH): AtomInterface {
        $name = $this->popExpression();
        $this->moveToNext(); // Skipping the name, set on (
        $current = $this->id;

        if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
            if ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON))) {
                $atom = 'Identifier';
            } else {
                $atom = 'Newcall';
            }
        } elseif ($getFullnspath === self::WITH_FULLNSPATH) {
            if (strtolower($name->code) === '\\define') {
                return $this->processDefineConstant($name);
            } elseif (strtolower($name->code) === 'define') {
                return $this->processDefineConstant($name);
            } elseif (strtolower($name->code) === '\\class_alias') {
                $atom = 'Classalias';
            } elseif (strtolower($name->code) === 'class_alias') {
                $atom = 'Classalias';
            } elseif ($name->fullnspath === '\\list') {
                $atom = 'List';
            } elseif ($this->nextIs(array($this->phptokens::T_ELLIPSIS)) &&
                      $this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS), 2)
                     ) {
                $atom = 'Callable';
            } else {
                $atom = 'Functioncall';
            }
        } else {
            if ($this->nextIs(array($this->phptokens::T_ELLIPSIS)) &&
                $this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS), 2)
                ) {
                    $atom = 'Callable';
                } else {
                    $atom = 'Methodcallname';
                }
        }

        $argumentsList = array();
        $functioncall = $this->processArguments($atom, array($this->phptokens::T_CLOSE_PARENTHESIS), $argumentsList);
        $this->makePhpdoc($functioncall);
        $argumentsFullcode           = $functioncall->fullcode;
        $functioncall->ws->opening  = '';
        $functioncall->ws->toargs   = '(' . $this->tokens[$current][4];
        $functioncall->ws->closing  = ')' . $this->tokens[$this->id][4];

        $functioncall->code      = $name->code;
        $functioncall->fullcode  = "{$name->fullcode}({$argumentsFullcode})";
        $functioncall->token     = $name->token;

        if ($atom === 'Newcall') {
            $this->getFullnspath($name, 'class', $functioncall);

            $this->calls->addCall(Calls::A_CLASS, $functioncall->fullnspath, $functioncall);
        } elseif ($atom === 'Classalias') {
            $functioncall->fullnspath = '\\classalias';

            $this->processDefineAsClassalias($argumentsList);
        } elseif (in_array($atom, array('Methodcallname', 'List', 'Closure'), \STRICT_COMPARISON)) {
            // literally, nothing
        } elseif (in_array(mb_strtolower($name->code), array('defined', 'constant'), \STRICT_COMPARISON)) {

            if ($argumentsList[0]->constant === self::CONSTANT_EXPRESSION &&
                !empty($argumentsList[0]->noDelimiter)) {

                $fullnspath = makeFullNsPath($argumentsList[0]->noDelimiter, \FNP_CONSTANT);
                if ($argumentsList[0]->noDelimiter[0] === '\\') {
                    $fullnspath = "\\$fullnspath";
                }
                $argumentsList[0]->fullnspath = $fullnspath;
                $this->calls->addCall(strpos($fullnspath, '::') === false ? 'const' : 'staticconstant', $fullnspath, $argumentsList[0]);
            }

            $functioncall->fullnspath = '\\' . mb_strtolower($name->code);
        } elseif ($atom === 'Callable') { // A first class callable
            $this->getFullnspath($name, 'function', $functioncall);
            $functioncall->absolute   = $name->absolute;

            $this->calls->addCall(Calls::FUNCTION, $functioncall->fullnspath, $functioncall);
        } elseif ($getFullnspath === self::WITH_FULLNSPATH) { // A functioncall
            $this->getFullnspath($name, 'function', $functioncall);
            $functioncall->absolute   = $name->absolute;

            $this->calls->addCall(Calls::FUNCTION, $functioncall->fullnspath, $functioncall);
        } else {
            throw new LoadError("Unprocessed atom in functioncall definition (its name) : $atom->atom : $this->filename : " . __LINE__);
        }

        $this->addLink($functioncall, $name, 'NAME');
        if ($name->atom === 'Name') {
            $this->runPlugins($name);
        }
        $this->pushExpression($functioncall);
        $this->checkPhpdoc();

        if ($functioncall->atom === 'Methodcallname') {
            $argumentsList[] = $name;
            $this->runPlugins($functioncall, $argumentsList);
        } elseif ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) &&
                   $this->nextIs(array($this->phptokens::T_CLOSE_TAG))      &&
                   $getFullnspath === self::WITH_FULLNSPATH ) {
             $this->processSemicolon();
        } else {
            $argumentsList[] = $name;
            $this->runPlugins($functioncall, $argumentsList);
            $functioncall = $this->processFCOA($functioncall);
        }

        return $functioncall;
    }

    private function processString(): AtomInterface {
        if ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR))) {
            $nsname = $this->processNsname();
            $this->runPlugins($nsname);
            return $this->processFCOA($nsname);
        } elseif ($this->nextIs(array($this->phptokens::T_NAME_QUALIFIED,
                                      $this->phptokens::T_NAME_RELATIVE,
                                      $this->phptokens::T_NAME_FULLY_QUALIFIED), 0)) {
            $nsname = $this->processNsname();
            $this->runPlugins($nsname);
            return $this->processFCOA($nsname);
        } elseif ($this->nextIs(array($this->phptokens::T_SEMICOLON,
                                      $this->phptokens::T_OPEN_CURLY,
                                      $this->phptokens::T_CLOSE_CURLY,
                                      $this->phptokens::T_COLON,
                                      $this->phptokens::T_OPEN_TAG,
                                      $this->phptokens::T_DOC_COMMENT,
                                      ), -1) &&
                   $this->nextIs(array($this->phptokens::T_COLON       ))) {
            return $this->processColon();
        } elseif (mb_strtolower($this->tokens[$this->id][1]) === 'self') {
            $string = $this->addAtom('Self', $this->id);
        } elseif (mb_strtolower($this->tokens[$this->id][1]) === 'parent') {
            $string = $this->addAtom('Parent', $this->id);
        } elseif (mb_strtolower($this->tokens[$this->id][1]) === 'list') {
            $string = $this->addAtom('Name', $this->id);
            $string->fullnspath = '\\list';
        } elseif ($this->contexts->isContext(Context::CONTEXT_NEW)) {
            // This catchs new A and new A()
            $string = $this->addAtom('Newcallname', $this->id);
            $this->runPlugins($string);
        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS ))) {
            $string = $this->addAtom('Name', $this->id);
         } elseif (in_array(mb_strtolower($this->tokens[$this->id][1]), array('true', 'false'), \STRICT_COMPARISON)) {
            $string = $this->addAtom('Boolean', $this->id);

            $string->noDelimiter = mb_strtolower($string->code) === 'true' ? 1 : '';
            $string->fullnspath = '\\' . mb_strtolower($string->code);
        } elseif (mb_strtolower($this->tokens[$this->id][1]) === 'null') {
            $string = $this->addAtom('Null', $this->id);
            $string->fullnspath = '\\null';
        } else {
            $string = $this->addAtom('Identifier', $this->id);
            $string->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            $string->ws->closing = '';
        }

        $string->fullcode   = $this->tokens[$this->id][1];
        $string->absolute   = self::NOT_ABSOLUTE;

        $this->pushExpression($string);

        if ($string->isA(array('Parent', 'Self', 'Static', 'Newcall', 'Newcallname'))) {
            if (!$this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
                $this->getFullnspath($string, 'class', $string);

                $this->calls->addCall(Calls::A_CLASS, $string->fullnspath, $string);
            }

            if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
                $string->count = 0;
            }
        } elseif ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON))     ||
                  $this->nextIs(array($this->phptokens::T_INSTANCEOF, -1))   ||
                  $this->nextIs(array($this->phptokens::T_NEW, -1))
            ) {
            if (!$this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
                $this->calls->addCall(Calls::A_CLASS, $string->fullnspath, $string);
            }
        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            // Nothing to do
        } else {
            $this->calls->addCall(Calls::CONST, $string->fullnspath, $string);
        }

        $this->runPlugins($string);

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            $string = $this->processFCOA($string);
        }

        return $string;
    }

    private function processPostPlusplus(AtomInterface $previous): AtomInterface {
        $this->moveToNext();
        $current = $this->id;
        $this->popExpression();
        $plusplus = $this->addAtom('Postplusplus', $this->id);

        $this->addLink($plusplus, $previous, 'POSTPLUSPLUS');

        $plusplus->fullcode = $previous->fullcode . $this->tokens[$this->id][1];
        $plusplus->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->pushExpression($plusplus);
        $this->runPlugins($plusplus, array('POSTPLUSPLUS' => $previous));

        $this->checkExpression();

        return $plusplus;
    }

    private function processPrePlusplus(): AtomInterface {
        $current = $this->id;

        $operator = $this->addAtom('Preplusplus', $this->id);
        $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$this->id][0]), 'PREPLUSPLUS');
        $operator = $this->popExpression();
        $operator->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $this->pushExpression($operator);

        $this->checkExpression();

        return $operator;
    }

    private function processStatic(): AtomInterface {
        $this->checkPhpdoc();
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON))   ||
            $this->nextIs(array($this->phptokens::T_INSTANCEOF), -1) ) {

            $identifier = $this->processSingle('Static');
            $identifier->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
            $this->pushExpression($identifier);
            $this->getFullnspath($identifier, 'class', $identifier);
            $this->calls->addCall(Calls::A_CLASS, $identifier->fullnspath, $identifier);

            return $identifier;
        }

         // static at the end of an expression
         if ($this->nextIs(array_merge(array($this->phptokens::T_OPEN_PARENTHESIS,
                                             $this->phptokens::T_PLUS,
                                             $this->phptokens::T_MINUS,
                                             ),
                                             $this->END_OF_EXPRESSION))) {
            $name = $this->addAtom('Static', $this->id);
            $name->fullcode   = $this->tokens[$this->id][1];

            $this->getFullnspath($name, 'class', $name);
            $name->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

            $this->pushExpression($name);

            return $this->processFCOA($name);
         }

         // static ?A $a = 1; (static property declaration)
         if ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR,
                                 $this->phptokens::T_QUESTION,
                                 $this->phptokens::T_STRING,
                                 $this->phptokens::T_NAMESPACE,
                                 $this->phptokens::T_ARRAY,
                                 $this->phptokens::T_CALLABLE,
                                 $this->phptokens::T_NAME_QUALIFIED,
                                 $this->phptokens::T_NAME_RELATIVE,
                                 $this->phptokens::T_NAME_FULLY_QUALIFIED,
                                 ))) {
            $current = $this->id;
            $option = $this->tokens[$this->id][1];

            $ppp = $this->addAtom('Ppp', $current);
            $returnTypes = $this->processTypehint($ppp);

            $this->processSGVariable($ppp);
            $ppp->ws->opening = '';

            $ppp->static = self::STATIC;
            $ppp->ws->static = $this->tokens[$current][1] . $this->tokens[$current][4];
            $ppp->visibility = 'none';
            $ppp->fullcode   = "$option {$returnTypes}$ppp->fullcode";
            $this->makePhpdoc($ppp);

            return $ppp;
        }

        if ($this->nextIs(array($this->phptokens::T_VARIABLE))) {
            if ($this->contexts->isContext(Context::CONTEXT_CLASS) &&
                !$this->contexts->isContext(Context::CONTEXT_FUNCTION)) {

                // something like public static
                $option = $this->tokens[$this->id][1];

                $ppp = $this->addAtom('Ppp', $current);
                $this->processSGVariable($ppp);
                $ppp->ws->opening = '';

                $void = $this->addAtomVoid();
                $this->addLink($ppp, $void, 'TYPEHINT');
                $void->rank = 0;

                if (empty($ppp->visibility)) {
                    $ppp->visibility = 'none';
                }
                $this->popExpression();

                $ppp->static = self::STATIC;
                $ppp->ws->static = $this->tokens[$current][1] . $this->tokens[$current][4];
                $ppp->ws->opening = '';
                $ppp->fullcode = "$option $ppp->fullcode";

                return $ppp;
            } else {
                $ppp = $this->processStaticVariable();

                $void = $this->addAtomVoid();
                $void->rank = 0;
                $this->addLink($ppp, $void, 'TYPEHINT');

                return $ppp;
            }
        }

        $static = $this->tokens[$this->id][1];

        $next = $this->processNext();

        $next->static   = self::STATIC;
        $next->ws->static = $this->tokens[$current][1] . $this->tokens[$current][4];

        $next->fullcode = "$static $next->fullcode";
        $this->makePhpdoc($next);
        return $next;
    }

    private function processSGVariable(AtomInterface $static, bool $promoted = self::PROMOTED_NOT): void {
        $current = $this->id;
        $rank = -1;

        $this->makePhpdoc($static);
        if ($static->isA(array('Global', 'Static'))) {
            $fullcodePrefix = $this->tokens[$this->id][1];
            $link = strtoupper($static->atom);
            $atom = $static->atom . 'definition';
        } else {
            $fullcodePrefix= array();
            $link = 'PPP';
            $atom = 'Propertydefinition';

            if (!isset($static->visibility)) {
                $static->visibility = 'none';
            }
            $fullcodePrefix = implode(' ', $fullcodePrefix);
        }
        $static->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        if (!isset($fullcodePrefix)) {
            $fullcodePrefix = $this->tokens[$current][1];
        }

        $finals = array($this->phptokens::T_SEMICOLON,
                        $this->phptokens::T_CLOSE_TAG,
                        $this->phptokens::T_CLOSE_PARENTHESIS,
                        );

        // This is only for promoted properties. Only one definition per PPP
        if ($promoted === self::PROMOTED) {
            $finals[] = $this->phptokens::T_COMMA;
        }

        $fullcode = array();
        $extras = array();
        --$this->id;
        do {
            $this->moveToNext();
            $this->checkPhpdoc();

           if ($this->nextIs(array($this->phptokens::T_AND,
                                   $this->phptokens::T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG))) {
                $reference = self::REFERENCE;
                $this->moveToNext();
            } else {
                $reference = self::NOT_REFERENCE;
            }

            if ($this->nextIs(array($this->phptokens::T_VARIABLE))) {
                $this->moveToNext();
                $element = $this->processSingle($atom);
                $this->makePhpdoc($element);

                if ($element->isA(array('Globaldefinition', 'Staticdefinition', 'Variabledefinition'))) {

                    if ($this->currentVariables->exists($element->code)) {
                        $definition = $this->currentVariables->get($this->tokens[$this->id][1]);
                    } else {
                        $definition = $this->addAtom('Variabledefinition');
                        $definition->code = $this->tokens[$this->id][1];
                        $definition->fullcode = $this->tokens[$this->id][1];
                        $this->addLink($this->currentMethod[count($this->currentMethod) - 1], $definition, 'DEFINITION');
                        $this->currentVariables->set($definition->code, $definition);
                    }

                    $this->addLink($definition, $element, 'DEFINITION');
                }

                if ($element->atom === 'Globaldefinition') {
                    $this->makeGlobal($element);
                }

                if ($this->nextIs(array($this->phptokens::T_EQUAL))) {
                    $this->moveToNext();
                    $element->ws->operator = '=' . $this->tokens[$this->id][4];
                    $default = $this->processExpression(array($this->phptokens::T_SEMICOLON,
                                                              $this->phptokens::T_CLOSE_TAG,
                                                              $this->phptokens::T_COMMA,
                                                              $this->phptokens::T_CLOSE_PARENTHESIS,
                                                              $this->phptokens::T_DOC_COMMENT,
                                                              ));
                } else {
                    $element->ws->operator = '';
                    $default = $this->addAtomVoid();
                }
            } else {
                // global $a[2] = 2 ?
               $element = $this->processExpression(array($this->phptokens::T_SEMICOLON,
                                                         $this->phptokens::T_CLOSE_TAG,
                                                         $this->phptokens::T_COMMA,
                                                         $this->phptokens::T_DOC_COMMENT,
                                                         ));
                $this->makePhpdoc($element);
                $this->popExpression();
                $default = $this->addAtomVoid();
                $element->ws->operator = '';
            }

            if ($reference === self::REFERENCE) {
                $element->fullcode  = '&' . $element->fullcode;
                $element->reference = self::REFERENCE;
            }

            $element->rank = ++$rank;
            $this->addLink($static, $element, $link);

            if ($atom === 'Propertydefinition') {
                // drop $
                $element->propertyname = ltrim($element->code, '$');
                $this->currentProperties[$element->propertyname] = $element;

                if ($this->currentClassTrait->getCurrent() !== ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                    $currentFNP = $this->currentClassTrait->getCurrent()->fullnspath;
                    $this->calls->addDefinition(Calls::STATICPROPERTY, $currentFNP . '::' . $element->code, $element);
                    $this->calls->addDefinition(Calls::PROPERTY, $currentFNP . '::' . ltrim($element->code, '$'), $element);
                }
            }

            $this->addLink($element, $default, 'DEFAULT');
            if ($default->atom === 'Void') {
                $this->runPlugins($element);
            } else {
                $element->fullcode .= " = {$default->fullcode}";
                $this->runPlugins($element, array('DEFAULT' => $default));
            }
            $fullcode[] = $element->fullcode;
            $extras[] = $element;
            $this->checkPhpdoc();
            if ($this->tokens[$this->id + 1][1] === ',') {
                $static->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            }

        } while (!$this->nextIs($finals));
        $static->ws->separators[] = '';

        $static->fullcode = (!empty($fullcodePrefix) ? $fullcodePrefix . ' ' : '') . implode(', ', $fullcode);
        $static->count    = $rank + 1;
        $this->runPlugins($static, $extras);

        $this->pushExpression($static);

        $this->checkExpression();
    }

    private function processStaticVariable(): AtomInterface {
        $variable = $this->addAtom('Static');
        $this->processSGVariable($variable);

        return $variable;
    }

    private function processGlobalVariable(): AtomInterface {
        $variable = $this->addAtom('Global');
        $this->processSGVariable($variable);

        return $variable;
    }

    private function processBracket(): AtomInterface {
        $current = $this->id;
        $bracket = $this->addAtom('Array', $current + 1);

        $variable = $this->popExpression();
        $this->addLink($bracket, $variable, 'VARIABLE');

        // Skip opening bracket
        $opening = $this->tokens[$this->id + 1][0];
        if ($opening === '{') {
            $closing = '}';
        } else {
            $closing = ']';
        }
        $bracket->ws->opening = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->moveToNext();
        $resetContext = false;
        if ($this->contexts->isContext(Context::CONTEXT_NEW)) {
            $resetContext = true;
            $this->contexts->toggleContext(Context::CONTEXT_NEW);
        }
        $index = $this->processExpression(array($this->phptokens::T_CLOSE_BRACKET,
                                                $this->phptokens::T_CLOSE_CURLY,
                                                ));

        if ($resetContext === true) {
            $this->contexts->toggleContext(Context::CONTEXT_NEW);
        }

        // Skip closing bracket
        $this->moveToNext();
        $this->addLink($bracket, $index, 'INDEX');

        if ($variable->code === '$GLOBALS' && !empty($index->noDelimiter)) {
            // Build the name of the global, dropping the fi
            $bracket->globalvar = '$' . $index->noDelimiter;

            $this->makeGlobal($index);
        }

        $bracket->fullcode  = $variable->fullcode . $opening . $index->fullcode . $closing ;
        $bracket->enclosing = self::NO_ENCLOSING;
        $bracket->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->pushExpression($bracket);
        $this->runPlugins($bracket, array('VARIABLE' => $variable,
                                          'INDEX'    => $index));

        $bracket = $this->processFCOA($bracket);
        $this->checkExpression();

        return $bracket;
    }

    private function processBlock(bool $standalone = self::STANDALONE_BLOCK): AtomInterface {
        $current = $this->id;
        $this->startSequence();
        $this->checkPhpdoc();

        // Case for {}
        if ($this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
            $void = $this->addAtomVoid();
            $void->ws->opening = '';
            $void->ws->closing = '';
            $this->addToSequence($void);
        } else {
            $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
            while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
                $this->processNext();
            }
            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

            $this->checkExpression();
        }

        $block = $this->sequence;
        $this->endSequence();

        $block->code     = '{}';
        $block->fullcode = static::FULLCODE_BLOCK;
        $block->token    = $this->getToken($this->tokens[$this->id][0]);
        $block->bracket  = self::BRACKET;
        $block->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $block->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->moveToNext(); // skip }

        $this->pushExpression($block);
        if ($standalone === self::STANDALONE_BLOCK) {
            $this->processSemicolon();
        }

        return $block;
    }

    private function processForblock(array $finals = array()): AtomInterface {
        $this->startSequence();
        $block = $this->sequence;

        if ($this->nextis($finals)) {
            $element                          = $this->addAtomVoid();
            $element->ws->opening             = '';
            $this->sequence->ws->separators[] = '';
        } else {
            do {
                $element = $this->processNext();

                if ($this->nextIs(array($this->phptokens::T_COMMA))) {
                    $element = $this->popExpression();
                    $this->addToSequence($element);
                    $this->sequence->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

                    $this->moveToNext();
                }
            } while (!$this->nextIs($finals));
            $this->sequence->ws->separators[] = '';
        }
        $this->popExpression();
        $this->addToSequence($element);

        $this->moveToNext();
        $sequence           = $this->sequence;
        $this->endSequence();
        $block->code        = $sequence->code;
        $block->fullcode    = self::FULLCODE_SEQUENCE;
        $block->token       = $this->getToken($this->tokens[$this->id][0]);
        $block->ws->closing = '';
        $block->ws->opening = '';

        if ($sequence->count === 1) {
            $block->fullcode = $element->fullcode;
        }

        return $block;
    }

    private function processFor(): AtomInterface {
        $current = $this->id;
        $for = $this->addAtom('For', $current);
        $this->makePhpdoc($for);
        $for->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                            $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        $this->moveToNext(); // Skip for

        $init = $this->processForblock(array($this->phptokens::T_SEMICOLON));
        $this->addLink($for, $init, 'INIT');
        $for->ws->init = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $final = $this->processForblock(array($this->phptokens::T_SEMICOLON));
        $this->addLink($for, $final, 'FINAL');
        $for->ws->final = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $increment = $this->processForblock(array($this->phptokens::T_CLOSE_PARENTHESIS));
        $this->addLink($for, $increment, 'INCREMENT');
        $for->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $isColon = $this->whichSyntax($current, $this->id + 1);

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $for->ws->toblock .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        }

        $block = $this->processFollowingBlock($isColon === self::ALTERNATIVE_SYNTAX ? array($this->phptokens::T_ENDFOR) : array());
        $this->addLink($for, $block, 'BLOCK');

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $fullcode = $this->tokens[$current][1] . '(' . $init->fullcode . ' ; ' . $final->fullcode . ' ; ' . $increment->fullcode . ') : ' . self::FULLCODE_SEQUENCE . ' ' . $this->tokens[$this->id + 1][1];
            // include endoforeach and the final ;
            $for->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            if ($this->tokens[$this->id + 2][1] === ';') {
                $for->ws->closing .= $this->tokens[$this->id + 2][1] . $this->tokens[$this->id + 2][4];
             }
        } else {
            $fullcode = $this->tokens[$current][1] . '(' . $init->fullcode . ' ; ' . $final->fullcode . ' ; ' . $increment->fullcode . ')' . ($block->bracket === self::BRACKET ? self::FULLCODE_BLOCK : self::FULLCODE_SEQUENCE);
            $for->ws->closing = '';
        }

        $for->fullcode    = $fullcode;
        $for->alternative = $isColon;

        $this->runPlugins($for, array('INIT'      => $init,
                                      'FINAL'     => $final,
                                      'INCREMENT' => $increment,
                                      'BLOCK'     => $block));

        $this->pushExpression($for);
        $this->finishWithAlternative($isColon);

        return $for;
    }

    private function processForeach(): AtomInterface {
        $current = $this->id;
        $foreach = $this->addAtom('Foreach', $current);
        $this->makePhpdoc($foreach);
        $foreach->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                                $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        $this->moveToNext(); // Skip foreach

        do {
            $source = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_AS)));

        $this->popExpression();
        $this->addLink($foreach, $source, 'SOURCE');

        $as = $this->tokens[$this->id + 1][1];
        $this->moveToNext(); // Skip as
        $foreach->ws->as = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $variablesStart = max(array_keys($this->atoms));

        while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS,
                                    $this->phptokens::T_DOUBLE_ARROW))) {
            $value = $this->processNext();
        }
        $this->popExpression();
        $valueFullcode = $value->fullcode;

        if ($this->nextIs(array($this->phptokens::T_DOUBLE_ARROW))) {
            $this->addLink($foreach, $value, 'INDEX');
            $variablesStart = max(array_keys($this->atoms));
            $index = $value;
            $this->moveToNext();
            $index->ws->operator = $index->ws->operator ?? $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
                $value = $this->processNext();
            }
            $this->popExpression();
            $valueFullcode .= " => {$value->fullcode}";
        }
        $this->addLink($foreach, $value, 'VALUE');

        // Warning : this is also connecting variables used for reading : foreach($a as [$b => $c]) { }
        $max = max(array_keys($this->atoms));
        $double = array($value->code => 1);
        for($i = $variablesStart + 1; $i < $max; ++$i) {
            if ($this->atoms[$i]->atom === 'Variable' && !isset($double[$this->atoms[$i]->code])) {
                $double[$this->atoms[$i]->code] = 1;
                $this->addLink($foreach, $this->atoms[$i], 'VALUE');
            }
        }
        unset($double);

        $this->moveToNext(); // Skip )
        $foreach->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $isColon = $this->whichSyntax($current, $this->id + 1);
        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $foreach->ws->toblock .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        }

        $block = $this->processFollowingBlock($isColon === self::ALTERNATIVE_SYNTAX ? array($this->phptokens::T_ENDFOREACH) : array());
        $this->addLink($foreach, $block, 'BLOCK');

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $fullcode = $this->tokens[$current][1] . '(' . $source->fullcode . ' ' . $as . ' ' . $valueFullcode . ') : ' . self::FULLCODE_SEQUENCE . ' endforeach';
            // include endoforeach and the final ;
            $foreach->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            if ($this->tokens[$this->id + 2][1] === ';') {
                $foreach->ws->closing .= $this->tokens[$this->id + 2][1] . $this->tokens[$this->id + 2][4];
             }
        } else {
            $fullcode = $this->tokens[$current][1] . '(' . $source->fullcode . ' ' . $as . ' ' . $valueFullcode . ')' . ($block->bracket === self::BRACKET ? self::FULLCODE_BLOCK : self::FULLCODE_SEQUENCE);
            $foreach->ws->closing = '';
        }

        $foreach->fullcode    = $fullcode;
        $foreach->alternative = $isColon;

        $extras = array('SOURCE'    => $source,
                        'VALUE'     => $value,
                        'BLOCK'     => $block);
        if (isset($index)) {
            $extras['INDEX'] = $index;
        }
        $this->runPlugins($foreach, $extras);

        $this->pushExpression($foreach);
        $this->finishWithAlternative($isColon);


        return $foreach;
    }

    private function processFollowingBlock(array $finals = array()): AtomInterface {
        $this->checkPhpdoc();
        if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
            $this->moveToNext();
            $current = $this->id;
            $block = $this->processBlock(self::RELATED_BLOCK);
            $block->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
            $block->ws->separators[] = '';
            $block->bracket = self::BRACKET;
            $this->popExpression(); // drop it
            $block->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            // Finish on the last token of the block

        } elseif ($this->nextIs(array($this->phptokens::T_COLON))) {
            $this->startSequence();
            $block = $this->sequence;
            $this->moveToNext(); // skip :

            while (!$this->nextIs($finals)) {
                $this->processNext();
            }

            $this->sequence->ws->opening = '';
            $this->sequence->ws->closing = '';

            $this->endSequence();

        } elseif ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {
            // void; One epxression block, with ;
            $this->startSequence();
            $block = $this->sequence;
            $block->ws->opening = '';
            $block->ws->separators[] = '';
            $block->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

            $void = $this->addAtomVoid();
            $this->addToSequence($void);
            $this->endSequence();
            $this->moveToNext();

        } elseif ($this->nextIs(array($this->phptokens::T_CLOSE_TAG,
                                      $this->phptokens::T_CLOSE_CURLY,
                                      $this->phptokens::T_CLOSE_PARENTHESIS,
                                      ))) {
            // Completely void (not even ;)
            $this->startSequence();
            $block = $this->sequence;

            $void = $this->addAtomVoid();
            $this->addToSequence($void);
            $this->endSequence();

        } else {
            // One expression only
            $this->startSequence();
            $block = $this->sequence;
            $current = $this->id;

            // This may include WHILE in the list of finals for do....while
            $finals = array_merge(array($this->phptokens::T_SEMICOLON,
                                        $this->phptokens::T_CLOSE_TAG,
                                        $this->phptokens::T_ELSE,
                                        $this->phptokens::T_END,
                                        $this->phptokens::T_CLOSE_CURLY,
                                        ), $finals);
            $specials = array($this->phptokens::T_IF,
                              $this->phptokens::T_FOREACH,
                              $this->phptokens::T_SWITCH,
                              $this->phptokens::T_FOR,
                              $this->phptokens::T_TRY,
                              $this->phptokens::T_WHILE,
                              );
            if ($this->nextIs($specials)) {
                $this->processNext();

                // backtrack on step, to avoid missing the next token
                --$this->id;
            } else {
                do {
                    $expression = $this->processNext();
                } while (!$this->nextIs($finals));
                $this->popExpression();
                if (!$this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
                    $this->addToSequence($expression);
                }
                $this->runPlugins($block, array($expression));
            }

            $this->sequence->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            $this->sequence->ws->opening = '';
            $this->sequence->ws->closing = '';

            $this->endSequence();

            // Finish on the final ; of the block
            $this->moveToNext();
        }

        return $block;
    }

    private function processDo(): AtomInterface {
        $current = $this->id;
        $dowhile = $this->addAtom('Dowhile', $this->id);
        $dowhile->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $block = $this->processFollowingBlock(array($this->phptokens::T_WHILE));
        $this->addLink($dowhile, $block, 'BLOCK');

        $while = $this->tokens[$this->id + 1][1];
        $this->moveToNext(); // Skip while
        $dowhile->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                                $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        $this->moveToNext(); // Skip (

        while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $condition = $this->processNext();
        }
        $this->moveToNext(); // skip )
        $this->popExpression();
        $this->addLink($dowhile, $condition, 'CONDITION');

        $dowhile->fullcode = $this->tokens[$current][1] . ( $block->bracket === self::BRACKET ? self::FULLCODE_BLOCK : self::FULLCODE_SEQUENCE) . $while . '(' . $condition->fullcode . ')';
        $dowhile->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->runPlugins($dowhile, array('CONDITION' => $condition,
                                          'BLOCK'     => $block));
        $this->pushExpression($dowhile);

        $this->checkExpression();

        return $dowhile;
    }

    private function processWhile(): AtomInterface {
        $current = $this->id;
        $while = $this->addAtom('While', $current);
        $while->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                              $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->moveToNext(); // Skip while

        do {
            $condition = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS)));
        $this->popExpression();
        $this->addLink($while, $condition, 'CONDITION');

        $this->moveToNext(); // Skip )
        $while->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        if (in_array($this->tokens[$this->id + 1][1], array(':'), \STRICT_COMPARISON)) {
            $while->ws->toblock .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        }

        $isColon = $this->whichSyntax($current, $this->id + 1);
        $block = $this->processFollowingBlock($isColon === self::ALTERNATIVE_SYNTAX ? array($this->phptokens::T_ENDWHILE) : array());
        $this->addLink($while, $block, 'BLOCK');

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $fullcode = $this->tokens[$current][1] . ' (' . $condition->fullcode . ') : ' . self::FULLCODE_SEQUENCE . ' ' . $this->tokens[$this->id + 1][1];
            $while->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4] .
                                  $this->tokens[$this->id + 2][1] . $this->tokens[$this->id + 2][4];
        } else {
            $fullcode = $this->tokens[$current][1] . ' (' . $condition->fullcode . ')' . ($block->bracket === self::BRACKET ? self::FULLCODE_BLOCK : self::FULLCODE_SEQUENCE);
            $while->ws->closing = '';
        }

        $while->fullcode    = $fullcode;
        $while->alternative = $isColon;

        $this->runPlugins($while, array('CONDITION' => $condition,
                                        'BLOCK'     => $block));

        $this->pushExpression($while);
        $this->finishWithAlternative($isColon);

        return $while;
    }

    private function processDeclare(): AtomInterface {
        $current = $this->id;
        $declare = $this->addAtom('Declare', $current);
        $fullcode = array();
        $declare->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] . $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->moveToNext(); // Skip declare
        $strictTypes = false;
        do {
            $this->moveToNext(); // Skip ( or ,
            $name = $this->processSingle('Name');

            $declaredefinition = $this->addAtom('Declaredefinition');
            $declaredefinition->ws->operator = '=' . $this->tokens[$this->id][4];
            $this->moveToNext(); // Skip =
            $config = $this->processNext();
            $this->popExpression();

            $this->addLink($declaredefinition, $name, 'NAME');
            $this->addLink($declaredefinition, $config, 'VALUE');

            $strictTypes |= strtolower($name->code) === 'strict_types';

            $this->addLink($declare, $declaredefinition, 'DECLARE');
            $declaredefinition->fullcode = $name->fullcode . ' = ' . $config->fullcode;
            $fullcode[] = $declaredefinition->fullcode;

            $this->moveToNext(); // Skip value
        } while ($this->nextIs(array($this->phptokens::T_COMMA), 0));

        if ($strictTypes === true) {
            $fullcode = $this->tokens[$current][1] . ' (' . implode(', ', $fullcode) . ') ';

            $this->moveToNext();
            $isColon = false;
        } else {
            $isColon = $this->whichSyntax($current, $this->id + 1);
            $declare->ws->endargs = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            if ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {
                $fullcode = $this->tokens[$current][1] . ' (' . implode(', ', $fullcode) . ') ;';
                $declare->ws->endargs = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                                        $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
                $this->moveToNext(); // skip ;
            } else {
                $block = $this->processFollowingBlock($isColon === self::ALTERNATIVE_SYNTAX ? array($this->phptokens::T_ENDDECLARE) : array());
                $this->addLink($declare, $block, 'BLOCK');

                if ($isColon === self::ALTERNATIVE_SYNTAX) {
                    $fullcode = $this->tokens[$current][1] . ' (' . implode(', ', $fullcode) . ') : ' . self::FULLCODE_SEQUENCE . ' ' . $this->tokens[$this->id + 1][1];
                } else {
                    $fullcode = $this->tokens[$current][1] . ' (' . implode(', ', $fullcode) . ') ' . self::FULLCODE_BLOCK;
                }
            }
        }

        $declare->fullcode    = $fullcode;
        $declare->ws->closing = '';
        $declare->alternative = $isColon;

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $this->moveToNext(); // Skip endforeach
            if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), 0)) {
                --$this->id;
            }
            $declare->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            $this->pushExpression($declare);
            $this->processSemicolon();
        } else {
            if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), 0)) {
                --$this->id;
            }

            $this->sequence->ws->separators[] = '';
            $this->addToSequence($declare);
        }

        return $declare;
    }

    private function processSwitchDefault(): AtomInterface {
        $current = $this->id;
        $default = $this->addAtom('Default', $current);
        $default->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                                $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        if  ($this->nextIs(array($this->phptokens::T_COLON,
                                 $this->phptokens::T_SEMICOLON))) {
            $this->moveToNext(); // Skip :
        }

        $default->fullcode = $this->tokens[$current][1] . ' : ' . self::FULLCODE_SEQUENCE;

        if ($this->nextIs(array($this->phptokens::T_CASE,
                                $this->phptokens::T_DEFAULT,
                                $this->phptokens::T_ENDSWITCH))) {
            $this->cases->add(array($default, null));
            $default->ws->final = false;

            return $default ;
        }
        $default->ws->final = true;

        $this->startSequence();
        if ($this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
            $void = $this->addAtomVoid();
            $void->ws->closing                = '';
            $this->addToSequence($void);
            $this->sequence->ws->separators[] = '';
            $this->sequence->ws->closing      = '';
            $this->sequence->ws->opening      = '';
        } else {
            while (!$this->nextis(array($this->phptokens::T_CLOSE_CURLY,
                                        $this->phptokens::T_CASE,
                                        $this->phptokens::T_DEFAULT,
                                        $this->phptokens::T_ENDSWITCH))) {
                $this->processNext();
            }
        }
        $code = $this->sequence;
        $this->endSequence();
        $code->ws->opening = '';

        foreach($this->cases->getAll() as $aCase) {
            $this->addLink($aCase[0], $code, 'CODE');

            if ($aCase[0]->atom === 'Default') {
                $this->runPlugins($aCase[0], array('CODE' => $code));
            } else {
                $this->runPlugins($aCase[0], array('CASE' => $aCase[1],
                                                   'CODE' => $code));
            }
        }

        $this->addLink($default, $code, 'CODE');
        $this->runPlugins($default, array('CODE' => $code));

        return $default;
    }

    // This process Case and Default inside a Match (also, trailing voids)
    private function processMatchCase(): AtomInterface {
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
            return $this->addAtomVoid();
        }

        if ($this->nextIs(array($this->phptokens::T_DEFAULT))) {
            $case = $this->addAtom('Default', $current);
            $item = null;
            $this->moveToNext();
            $case->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                                 $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        } else {
            $case = $this->addAtom('Case', $current);

            $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
            do {
                $item = $this->processNext();
            } while (!$this->nextIs(array($this->phptokens::T_DOUBLE_ARROW,
                                          $this->phptokens::T_COMMA)));
            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

            $this->popExpression();
            $this->addLink($case, $item, 'CASE');

            $case->ws->opening = '';
            $case->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        }
        $this->cases->add(array($case, $item));
        $case->ws->final = $this->nextIs(array($this->phptokens::T_CLOSE_CURLY));

        if ($this->nextIs(array($this->phptokens::T_COMMA))) {
            $this->moveToNext();
            if (!$this->nextIs(array($this->phptokens::T_DOUBLE_ARROW))) {
                return $case;
            }
        }
        $this->moveToNext(); // Skip => or ,

        $this->startSequence();
        do {
            $expression = $this->processNext();
        } while (!$this->nextis(array($this->phptokens::T_CLOSE_CURLY,
                                      $this->phptokens::T_COMMA)));

        if ($this->nextIs(array($this->phptokens::T_COMMA))) {
            $this->moveToNext();
        }
        $this->sequence->ws->separators[] = '';
        $this->addToSequence($expression);
        $code = $this->sequence;
        $this->endSequence();

        foreach($this->cases->getAll() as $aCase) {
            $this->addLink($aCase[0], $code, 'CODE');

            if ($aCase[0]->atom === 'Default') {
                $this->runPlugins($aCase[0], array( 'CODE' => $code));
            } else {
                $this->runPlugins($aCase[0], array('CASE' => $aCase[1],
                                                   'CODE' => $code));
            }
        }

        $children = array('CODE' => $code);
        if ($case->atom === 'Case') {
            $children['CASE'] = $item;
        }
        $this->runPlugins($case, $children);

        return $case;
    }

    private function processSwitchCase(): AtomInterface {
        $current = $this->id;
        $case = $this->addAtom('Case', $current);
        $case->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        while (!$this->nextis(array($this->phptokens::T_COLON,
                                    $this->phptokens::T_SEMICOLON,
                                    $this->phptokens::T_CLOSE_TAG))) {
            $item = $this->processNext();
        }
        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
        $case->ws->toblock = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->popExpression();
        $this->addLink($case, $item, 'CASE');

        if  ($this->nextIs(array($this->phptokens::T_COLON,
                                 $this->phptokens::T_SEMICOLON))) {
            $this->moveToNext(); // Skip :
        }

        $case->fullcode = $this->tokens[$current][1] . ' ' . $item->fullcode . ' : ' . self::FULLCODE_SEQUENCE . ' ';

        if ($this->nextIs(array($this->phptokens::T_CASE,
                                $this->phptokens::T_DEFAULT,
                                $this->phptokens::T_ENDSWITCH))) {
            $this->cases->add(array($case, $item));
            $case->ws->final = false;

            return $case;
        }
        $case->ws->final = true;

        $this->startSequence();
        if ($this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
            $void = $this->addAtomVoid();
            $void->ws->closing                = '';
            $this->addToSequence($void);
            $this->sequence->ws->separators[] = '';
            $this->sequence->ws->closing      = '';
            $this->sequence->ws->opening      = '';
        } else {
            while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY,
                                        $this->phptokens::T_CASE,
                                        $this->phptokens::T_DEFAULT,
                                        $this->phptokens::T_ENDSWITCH))) {
                $this->processNext();
            }
        }

        $code = $this->sequence;
        $this->endSequence();
        $code->ws->opening = '';

        foreach($this->cases->getAll() as $aCase) {
            $code->ws->opening = '';
            $this->addLink($aCase[0], $code, 'CODE');

            if ($aCase[0]->atom === 'Default') {
                $this->runPlugins($aCase[0], array('CODE' => $code));
            } else {
                $this->runPlugins($aCase[0], array('CASE' => $aCase[1],
                                                   'CODE' => $code));
            }
        }

        $this->addLink($case, $code, 'CODE');

        $this->runPlugins($case, array( 'CASE' => $item,
                                        'CODE' => $code));

        return $case;
    }

    private function processSwitchCaseDefault(): AtomInterface {
        $this->checkPhpdoc();

        // skip { or :
        $this->moveToNext();

        switch($this->tokens[$this->id][0]) {
            case $this->phptokens::T_CASE:
                $case = $this->processSwitchCase();
                break;

            case $this->phptokens::T_DEFAULT:
                $case = $this->processSwitchDefault();
                break;

            case $this->phptokens::T_CLOSE_TAG:
                $case = $this->processClosingTag();
                break;

            default:
                assert(false, 'Switch case : not a case nor a default : ' . print_r($this->tokens[$this->id], true) . "\n{$this->filename}\n");
        }

        return $case;
    }

    private function processSwitch(): AtomInterface {
        $current = $this->id;
        $switch = $this->addAtom('Switch', $current);
        $switch->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] . $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        $this->moveToNext(); // Skip (
        $this->cases->push();

        do {
            $name = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS)));
        $this->popExpression();
        $this->addLink($switch, $name, 'CONDITION');
        $this->moveToNext(); // skip )
        $switch->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] . $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $isColon = $this->whichSyntax($current, $this->id + 1);

        $cases = $this->addAtom('Sequence', $current);
        $cases->code         = self::FULLCODE_SEQUENCE;
        $cases->fullcode     = self::FULLCODE_SEQUENCE;
        $cases->bracket      = $isColon === true ? self::NOT_BRACKET : self::BRACKET;

        $this->addLink($switch, $cases, 'CASES');
        $extraCases = array();

        $rank = -1;
        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $void = $this->addAtomVoid();
            $this->addLink($cases, $void, 'EXPRESSION');
            $void->rank = $rank;
            $extraCases[] = $void;

            $this->moveToNext();
        } else {
            if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
                $this->moveToNext();
                $finals = array($this->phptokens::T_CLOSE_CURLY);
            } else {
                $this->moveToNext(); // skip :
                $finals = array($this->phptokens::T_ENDSWITCH);
            }
            while (!$this->nextIs($finals)) {
                // process case or default.
                $case = $this->processSwitchCaseDefault();

                $this->popExpression();
                $this->addLink($cases, $case, 'EXPRESSION');
                $case->rank = ++$rank;
                $extraCases[] = $case;
                $cases->ws->separators[] = ''; //$this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            }
        }
        $this->moveToNext();
        $cases->count = $rank + 1;
        $cases->ws->opening = '';
        $cases->ws->closing = '';

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $fullcode = $this->tokens[$current][1] . ' (' . $name->fullcode . ') :' . self::FULLCODE_SEQUENCE . ' ' . $this->tokens[$this->id][1];
            $switch->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        } else {
            $fullcode = $this->tokens[$current][1] . ' (' . $name->fullcode . ')' . self::FULLCODE_BLOCK;
            $switch->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        }

        $switch->fullcode    = $fullcode;
        $switch->alternative = $isColon;
        $this->runPlugins($cases, $extraCases);

        $this->runPlugins($switch, array('CONDITION' => $name,
                                         'CASES'     => $cases, ));

        $this->pushExpression($switch);
        $this->finishWithAlternative($isColon);

        $this->cases->pop();

        return $switch;
    }

    private function processMatch(): AtomInterface {
        $current = $this->id;
        $match = $this->addAtom('Match', $current);
        $match->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                              $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $this->moveToNext(); // Skip (
        $this->cases->push();

        do {
            $name = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS)));
        $this->popExpression();
        $this->addLink($match, $name, 'CONDITION');

        $cases = $this->addAtom('Sequence', $current);
        $cases->code     = self::FULLCODE_SEQUENCE;
        $cases->fullcode = self::FULLCODE_SEQUENCE;
        $cases->bracket  = self::BRACKET;

        $this->addLink($match, $cases, 'CASES');
        $extraCases = array();
        $this->moveToNext();
        $match->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                              $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

        $rank = -1;
        if ($this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            // case of an empty Match
            $void = $this->addAtomVoid();
            $this->addLink($cases, $void, 'EXPRESSION');
            $void->rank = $rank;
            $extraCases[] = $void;

            $this->moveToNext();
        } else {
            if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
                $this->moveToNext();
                $finals = array($this->phptokens::T_CLOSE_CURLY);
            } else {
                $this->moveToNext(); // skip :
                $finals = array($this->phptokens::T_ENDSWITCH);
            }
            do {
                $case = $this->processMatchCase();

                $this->popExpression();
                $this->addLink($cases, $case, 'EXPRESSION');
                $case->rank = ++$rank;
                $extraCases[] = $case;
                if ($this->nextIs(array(','), 0)) {
                    $cases->ws->separators[] = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                } else {
                    $cases->ws->separators[] = '';
                }
            } while (!$this->nextis($finals));
        }
        $this->moveToNext();
        $cases->count = $rank + 1;
        $cases->ws->opening = '';

        $fullcode = $this->tokens[$current][1] . ' (' . $name->fullcode . ')' . self::FULLCODE_BLOCK;
        $match->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $match->fullcode    = $fullcode;

        $this->runPlugins($cases, $extraCases);
        $this->runPlugins($match, array('CONDITION' => $name,
                                        'CASES'     => $cases, ));

        $this->pushExpression($match);

        $this->cases->pop();

        return $match;
    }

    private function processIfthen(): AtomInterface {
        $this->checkPhpdoc();
        $current = $this->id;
        $ifthen = $this->addAtom('Ifthen', $current);
        $this->makePhpdoc($ifthen);
        $ifthen->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4] .
                               $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
        $this->moveToNext(); // Skip (

        do {
            $condition = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS)));

        $this->popExpression();
        $this->addLink($ifthen, $condition, 'CONDITION');
        $extras = array('CONDITION' => $condition);

        $this->moveToNext(); // Skip )
        $ifthen->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        if (in_array($this->tokens[$this->id + 1][1], array(':'), \STRICT_COMPARISON)) {
            $ifthen->ws->toblock .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        }

        $isInitialIf = $this->tokens[$current][0] === $this->phptokens::T_IF;
        $isColon = $this->whichSyntax($current, $this->id + 1);

        $then = $this->processFollowingBlock(array($this->phptokens::T_ENDIF,
                                                   $this->phptokens::T_ELSE,
                                                   $this->phptokens::T_ELSEIF,
                                                   ));
        $this->addLink($ifthen, $then, 'THEN');
        $extras['THEN'] = $then;

        $this->checkPhpdoc();
        // Managing else case
        if ($this->nextIs(array($this->phptokens::T_END,
                                $this->phptokens::T_CLOSE_TAG), 0)) {
            $elseFullcode = '';
            // No else, end of a script
            --$this->id;
            // Back up one unit to allow later processing for sequence
        } elseif ($this->nextIs(array($this->phptokens::T_ELSEIF))){
            $this->moveToNext();
            $this->checkPhpdoc();

            $elseif = $this->processIfthen();
            $this->addLink($ifthen, $elseif, 'ELSE');
            $extras['ELSE'] = $elseif;
            $ifthen->ws->else = '';

            $elseFullcode = $elseif->fullcode;

        } elseif ($this->nextIs(array($this->phptokens::T_ELSE))){
            $this->moveToNext(); // Skip else
            $elseFullcode = $this->tokens[$this->id][1];
            $ifthen->ws->else = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            if (in_array($this->tokens[$this->id + 1][1], array(':'), \STRICT_COMPARISON)) {
                $ifthen->ws->else .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            }

            $else = $this->processFollowingBlock(array($this->phptokens::T_ENDIF));

            $this->addLink($ifthen, $else, 'ELSE');
            $extras['ELSE'] = $else;

            if ($isColon === self::ALTERNATIVE_SYNTAX) {
                $elseFullcode .= ' :';
            }
            $elseFullcode .= $else->fullcode;
        } else {
            $elseFullcode = '';
        }

        if ($isInitialIf === true && $isColon === self::ALTERNATIVE_SYNTAX) {
            if ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {
                $this->moveToNext(); // skip ;
            }
            $this->moveToNext(); // skip ;
        }

        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $fullcode = $this->tokens[$current][1] . '(' . $condition->fullcode . ') : ' . $then->fullcode . $elseFullcode . ($isInitialIf === true ? ' endif' : '');
            $ifthen->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        } else {
            $fullcode = $this->tokens[$current][1] . '(' . $condition->fullcode . ')' . $then->fullcode . $elseFullcode;
            $ifthen->ws->closing = '';
        }

        $ifthen->fullcode    = $fullcode;
        $ifthen->alternative = $isColon;

        $this->runPlugins($ifthen, $extras);

        if ($this->tokens[$current][0] === $this->phptokens::T_IF) {
            $this->pushExpression($ifthen);
            $this->finishWithAlternative($isColon);
        }

        return $ifthen;
    }

    private function checkPhpdoc(): void {
        if (!isset($this->tokens[$this->id + 1])) {
            return;
        }

        while($this->nextIs(array($this->phptokens::T_DOC_COMMENT))){
            ++$this->id;
            $this->processPhpdoc();
        }
    }

    private function checkAttribute(): void {
        while($this->nextIs(array($this->phptokens::T_ATTRIBUTE))){
            ++$this->id;
            $this->processAttribute();
        }
    }

    private function processParenthesis(): AtomInterface {
        $current = $this->id;
        $parenthese = $this->addAtom('Parenthesis', $current);

        while (!$this->nextIs(array($this->phptokens::T_CLOSE_PARENTHESIS))) {
            $this->processNext();
        }

        $code = $this->popExpression();
        $this->addLink($parenthese, $code, 'CODE');

        $parenthese->fullcode    = '(' . $code->fullcode . ')';
        $parenthese->noDelimiter = $code->noDelimiter;
        $parenthese->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $parenthese->ws->closing = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        $this->runPlugins($parenthese, array('CODE' => $code));

        $this->pushExpression($parenthese);
        $this->moveToNext(); // Skipping the )

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            $parenthese = $this->processFCOA($parenthese);
        }

        return $parenthese;
    }

    private function processExit(): AtomInterface {
        $current = $this->id;
        if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {

            $this->moveToNext();
            $functioncall = $this->processArguments('Exit',
                                                    array($this->phptokens::T_SEMICOLON,
                                                          $this->phptokens::T_CLOSE_TAG,
                                                          $this->phptokens::T_CLOSE_PARENTHESIS,
                                                          $this->phptokens::T_CLOSE_BRACKET,
                                                          $this->phptokens::T_CLOSE_CURLY,
                                                          $this->phptokens::T_COLON,
                                                          $this->phptokens::T_END,
                                                          ));
            $functioncall->position = $this->tokens[$current][3];
            $argumentsFullcode = $functioncall->fullcode;
            $argumentsFullcode = "($argumentsFullcode)";

            $functioncall->code        = $this->tokens[$current][1];
            $functioncall->fullcode    = $this->tokens[$current][1] . $argumentsFullcode;
            $functioncall->fullnspath  = '\\' . mb_strtolower($this->tokens[$current][1]);
            $functioncall->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4] . $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
            $functioncall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->pushExpression($functioncall);
            $this->runPlugins($functioncall);

            $this->checkExpression();

            return $functioncall;
        } else {
            $functioncall = $this->addAtom('Exit', $this->id);

            $functioncall->fullcode    = $this->tokens[$this->id][1] . ' ';
            $functioncall->count       = 0;
            $functioncall->fullnspath  = '\\' . mb_strtolower($functioncall->code);
            $functioncall->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
            $functioncall->ws->closing = '';

            $void = $this->addAtomVoid();
            $void->rank = 0;

            $this->addLink($functioncall, $void, 'ARGUMENT');

            if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) &&
                 $this->nextIs(array($this->phptokens::T_CLOSE_TAG,
                                     $this->phptokens::T_COMMA))) {
                $this->processSemicolon();
            }

            $this->pushExpression($functioncall);
            $this->checkExpression();

            return $functioncall;
        }
    }

    private function processArrayLiteral(): AtomInterface {
        $current = $this->id;

        $argumentsList = array();
        if ($this->tokens[$current][0] === $this->phptokens::T_ARRAY) {
            $this->moveToNext(); // Skipping the name, set on (
            $array = $this->processArguments('Arrayliteral', array(), $argumentsList);
            $argumentsFullcode = $array->fullcode;
            $array->token    = 'T_ARRAY';
            $array->fullcode = $this->tokens[$current][1] . '(' . $argumentsFullcode . ')';
            $array->ws->opening   = $this->tokens[$current][1] . $this->tokens[$current][4] .
                                    $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
            $array->ws->closing   = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        } else {
            $bracket = 1;
            $id = $this->id;
            while($bracket > 0) {
                ++$id;
                if (!isset($this->tokens[$id])) {
                    throw new LoadError('File is finished in Arraylist :' . $this->filename . ':' . $this->tokens[$current][2]);
                }

                if ($this->tokens[$id][0] === $this->phptokens::T_CLOSE_BRACKET) {
                    --$bracket;
                } elseif ($this->tokens[$id][0] === $this->phptokens::T_OPEN_BRACKET) {

                    ++$bracket;
                }
            }

            if ($this->tokens[$id + 1][0] === $this->phptokens::T_EQUAL ||
                $this->tokens[$current - 1][0] === $this->phptokens::T_AS ||
                $this->contexts->isContext(Context::CONTEXT_LIST)
                ) {

                $this->contexts->nestContext(Context::CONTEXT_LIST);
                $this->contexts->toggleContext(Context::CONTEXT_LIST);
                $array = $this->processArguments('List', array($this->phptokens::T_CLOSE_BRACKET), $argumentsList);
                $this->contexts->toggleContext(Context::CONTEXT_LIST);
                $this->contexts->exitContext(Context::CONTEXT_LIST);
                $argumentsFullcode = $array->fullcode;

                // This is a T_LIST !
                $array->token      = 'T_OPEN_BRACKET';
                $array->fullnspath = '\list';
                $array->fullcode   = "[$argumentsFullcode]";
                $array->ws->opening   = $this->tokens[$current][1] . $this->tokens[$current][4];
                $array->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                $array->ws->toargs    = '';
            } else {
                $array = $this->processArguments('Arrayliteral', array($this->phptokens::T_CLOSE_BRACKET), $argumentsList);
                $argumentsFullcode = $array->fullcode;

                $array->token         = 'T_OPEN_BRACKET';
                $array->fullcode      = "[$argumentsFullcode]";
                $array->ws->opening   = $this->tokens[$current][1] . $this->tokens[$current][4];
                $array->ws->closing   = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            }
        }

        $array->code      = $this->tokens[$current][1];
        $this->runPlugins($array, $argumentsList);

        $this->pushExpression($array);

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            $array = $this->processFCOA($array);
        }

        return $array;
    }

    private function processTernary(): AtomInterface {
        $current = $this->id;
        $condition = $this->popExpression();
        $ternary = $this->addAtom('Ternary', $current);
        $ternary->ws->operator = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $names = array($this->phptokens::T_STRING,
                       $this->phptokens::T_NAME_FULLY_QUALIFIED,
                       $this->phptokens::T_NAME_RELATIVE,
                       $this->phptokens::T_NAME_QUALIFIED,
        );

        if ($this->nextIs($names) &&
            $this->nextIs(array($this->phptokens::T_COLON), 2)) {

            if (in_array(mb_strtolower($this->tokens[$this->id + 1][1]), array('true', 'false', '\true', '\false'), \STRICT_COMPARISON)) {
                $this->moveToNext();
                $then = $this->processSingle('Boolean');
                $this->runPlugins($then);
            } elseif (mb_strtolower($this->tokens[$this->id + 1][1]) === 'null') {
                $this->moveToNext();
                $then = $this->processSingle('Null');
                $this->runPlugins($then);
            } else {
                $then = $this->processNextAsIdentifier();
                $this->getFullnspath($then, 'const', $then);
                $this->calls->addCall(Calls::CONST, $then->fullnspath, $then);
            }
        } else {
            $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
            if ($this->nextIs(array($this->phptokens::T_COLON))) {
                $then = $this->addAtomVoid();
            } else {
                do {
                    $then = $this->processNext();
                } while (!$this->nextIs(array($this->phptokens::T_COLON)) );
            }

            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
            $this->popExpression();
        }

        $this->moveToNext(); // Skip colon
        $ternary->ws->else = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        // special cases for T_STRING
        if ($this->nextIs($names) &&
            $this->nextIs(array($this->phptokens::T_COLON), 2)) {
            if (in_array(mb_strtolower($this->tokens[$this->id + 1][1]), array('true', 'false'), \STRICT_COMPARISON)) {
                $this->moveToNext();
                $else = $this->processSingle('Boolean');
                $this->runPlugins($else);
            } elseif (mb_strtolower($this->tokens[$this->id + 1][1]) === 'null') { // should also check on T_STRING
                $this->moveToNext();
                $else = $this->processSingle('Null');
                $this->runPlugins($else);
            } else {
                $else = $this->processNextAsIdentifier();
            }
        } else {
            $finals = $this->precedence->get($this->tokens[$this->id][0]);
            $finals[] = $this->phptokens::T_COLON; // Added from nested Ternary
            $finals[] = $this->phptokens::T_CLOSE_TAG;

            $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
            do {
                $else = $this->processNext();
            } while (!$this->nextIs($finals));
            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

            $this->popExpression();
        }

        if ($then->isA(array('Identifier', 'Nsname'))) {
            $this->calls->addCall(Calls::CONST, $then->fullnspath, $then);
        }
        $this->addLink($ternary, $condition, 'CONDITION');
        $this->addLink($ternary, $then, 'THEN');
        $this->addLink($ternary, $else, 'ELSE');

        $ternary->fullcode = $condition->fullcode . ' ?' . ($then->atom === 'Void' ? '' : ' ' . $then->fullcode . ' ' ) . ': ' . $else->fullcode;
        $this->runPlugins($ternary, array('CONDITION' => $condition,
                                          'THEN'      => $then,
                                          'ELSE'      => $else,
                                          ));

        $this->pushExpression($ternary);

        $this->checkExpression();

        return $ternary;
    }

    //////////////////////////////////////////////////////
    /// processing single tokens
    //////////////////////////////////////////////////////
    private function processSingle(string $atomName): AtomInterface {
        $atom = $this->addAtom($atomName, $this->id);
        $atom->fullcode = $this->tokens[$this->id][1];

        if ($atomName === 'Phpvariable' && in_array($atom->code, array('$GLOBALS', '$_SERVER', '$_REQUEST', '$_POST', '$_GET', '$_FILES', '$_ENV', '$_COOKIE', '$_SESSION'), \STRICT_COMPARISON)) {
            $this->makeGlobal($atom);
        } elseif (!in_array($atomName, array('Parametername', 'Parameter', 'Staticpropertyname', 'Propertydefinition', 'Globaldefinition', 'Staticdefinition', 'This'), \STRICT_COMPARISON) &&
            $this->nextIs(array($this->phptokens::T_VARIABLE), 0)) {
            if ($this->currentVariables->exists($atom->code)) {
                $this->addLink($this->currentVariables->get($atom->code), $atom, 'DEFINITION');
            } else {
                $definition = $this->addAtom('Variabledefinition');
                $definition->code = $atom->code;
                $definition->fullcode = $atom->fullcode;
                $this->addLink($this->currentMethod[count($this->currentMethod) - 1], $definition, 'DEFINITION');
                $this->currentVariables->set($atom->code, $definition);

                $this->addLink($definition, $atom, 'DEFINITION');

                if (!$this->contexts->isContext(Context::CONTEXT_FUNCTION)) {
                    $this->makeGlobal($definition);
                }
            }
        }

        return $atom;
    }

    private function processInlinehtml(): AtomInterface {
        $inlineHtml = $this->processSingle('Inlinehtml');

        if ($this->id > 0 && $this->nextIs(array($this->phptokens::T_CLOSE_TAG), -1)) {
            $inlineHtml->ws->opening = $this->tokens[$this->id - 1][1];
        } else {
            $inlineHtml->ws->opening = '';
        }

        $inlineHtml->ws->closing = '';

        $this->sequence->ws->separators[] = '';

        return $inlineHtml;
    }

    private function processNamespaceBlock(): AtomInterface {
        $this->startSequence();

        while (!$this->nextIs(array($this->phptokens::T_CLOSE_TAG,
                                    $this->phptokens::T_NAMESPACE,
                                    $this->phptokens::T_END,
                                    ))) {
            $this->processNext();

            if ($this->nextIs(array($this->phptokens::T_NAMESPACE)) &&
                $this->nextIs(array($this->phptokens::T_NS_SEPARATOR), 2)) {

                $this->processNext();
            }
        }
        $block = $this->sequence;
        $this->endSequence();

        $block->code     = ' ';
        $block->fullcode = ' ' . self::FULLCODE_SEQUENCE . ' ';
        $block->token    = $this->getToken($this->tokens[$this->id][0]);

        return $block;
    }

    private function processNamespace(): AtomInterface {
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR))) {
            $nsname = $this->processOneNsname();

            $this->pushExpression($nsname);

            return $this->processFCOA($nsname);
        }

        if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
            $name = $this->addAtomVoid();
            $name->ws->closing = '';
        } else {
            $name = $this->processNextAsIdentifier();
            $name->fullnspath = ($name->fullcode[0] === '\\' ? '' : '\\') . mb_strtolower($name->fullcode);
        }

        $namespace = $this->addAtom('Namespace', $current);
        $this->makePhpdoc($namespace);
        $this->addLink($namespace, $name, 'NAME');
        $this->setNamespace($name->fullcode === ' ' ? self::NO_NAMESPACE : $name->fullcode);

        // Here, we make sure namespace is encompassing the next elements.
        if ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {

            // Process block
            $this->moveToNext(); // Skip ; to start actual sequence
            if ($this->nextIs(array($this->phptokens::T_END))) {
                $namespace->ws->toblock = '';

                $void = $this->addAtomVoid();
                $block = $this->addAtom('Sequence', $this->id);
                $block->code       = '{}';
                $block->fullcode   = self::FULLCODE_BLOCK;
                $block->bracket    = self::NOT_BRACKET;
                $block->ws->opening      = ';';
                $block->ws->closing      = '';

                $this->addLink($block, $void, 'EXPRESSION');
            } else {
                $namespace->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                $block = $this->processNamespaceBlock();
                $block->ws->opening      = '';
            }
            $this->addLink($namespace, $block, 'BLOCK');
            $this->addToSequence($namespace);
            $block = ';';
            $namespace->ws->closing = '';
        } else {
            // Process block
            $block = $this->processFollowingBlock(array($this->phptokens::T_CLOSE_CURLY));
            $this->addLink($namespace, $block, 'BLOCK');

            $this->addToSequence($namespace);

            $block = self::FULLCODE_BLOCK;
            $namespace->ws->toblock = '';
            $namespace->ws->closing = '';
        }
        $this->setNamespace(self::NO_NAMESPACE);

        $namespace->fullcode   = $this->tokens[$current][1] . ' ' . $name->fullcode . $block;
        $namespace->fullnspath = $name->atom === 'Void' ? '\\' : $name->fullnspath;
        $namespace->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->sequence->ws->separators[] = '';
        return $namespace;
    }

    private function processAlias(string $useType): AtomInterface {
        $current = $this->id;
        $as = $this->addAtom('As', $current);
        $as->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $left = $this->popExpression();
        $this->addLink($as, $left, 'NAME');

        $right = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        $right->fullnspath = '\\' . mb_strtolower($right->code);
        $this->addLink($as, $right, 'AS');

        $as->fullcode = $left->fullcode . ' ' . $this->tokens[$this->id - 1][1] . ' ' . $right->fullcode;

        $this->addNamespaceUse($left, $as, $useType, $as);

        return $as;
    }

    private function processAsTrait(): AtomInterface {
        $current = $this->id;
        $as = $this->addAtom('As', $current);

        // special case for use t, t2 { as as yes; }
        if ($this->nextIs(array($this->phptokens::T_AS))) {
            $left = $this->processNextAsIdentifier();
        } else {
            $left = $this->popExpression();
        }

        $as->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->getFullnspath($left, 'staticmethod', $left);
        $this->calls->addCall(Calls::STATICMETHOD, $left->fullnspath, $left);

        $this->addLink($as, $left, 'NAME');
        $fullcode = array($left->fullcode, $this->tokens[$current][1]);

        if ($this->nextis(array($this->phptokens::T_PRIVATE,
                                $this->phptokens::T_PUBLIC,
                                $this->phptokens::T_PROTECTED,
                                ))) {
            $fullcode[] = $this->tokens[$this->id + 1][1];
            $as->visibility = strtolower($this->tokens[$this->id + 1][1]);
            $as->ws->visibility = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            $this->moveToNext();
        }

        if (!$this->nextIs(array($this->phptokens::T_SEMICOLON))) {
            $alias = $this->processNextAsIdentifier();
            $this->addLink($as, $alias, 'AS');
            $fullcode[] = $alias->fullcode;
        }

        $as->fullcode = implode(' ', $fullcode);

        $this->pushExpression($as);

        return $as;
    }

    private function processInsteadof(): AtomInterface {
        $insteadof = $this->processOperator('Insteadof', $this->precedence->get($this->tokens[$this->id][0]), array('NAME', 'INSTEADOF'));
        while ($this->nextIs(array($this->phptokens::T_COMMA))) {
            $this->moveToNext();
            $nsname = $this->processOneNsname();

            $this->addLink($insteadof, $nsname, 'INSTEADOF');
        }
        $insteadof->ws->closing = '';
        return $insteadof;
    }

    private function processUse(): AtomInterface {
        if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
            return $this->processUseNamespace();
        } else {
            return $this->processUseTrait();
        }
    }

    private function processUseNamespace(): AtomInterface {
        $current = $this->id;
        $use = $this->addAtom('Usenamespace', $current);
        $use->use = 'class';
        $use->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $this->makePhpdoc($use);

        $fullcode = array();

        $use->ws->operator = '';
        // use const
        if ($this->nextIs(array($this->phptokens::T_CONST))) {
            $use->ws->operator = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            $this->moveToNext();

            $use->use = 'const';
        }

        // use function
        if ($this->nextIs(array($this->phptokens::T_FUNCTION))) {
            $use->ws->operator = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            $this->moveToNext();

            $use->use = 'function';
        }

        $rank = -1;
        $useType = $use->use;
        --$this->id;
        $usesDefinitions = array();
        do {
            ++$rank;
            $prefix = '';
            $this->moveToNext();
            $this->checkPhpdoc();
            $namespace = $this->processOneNsname(self::WITHOUT_FULLNSPATH);
            // Default case : use A\B
            $alias = $namespace;
            $origin = $namespace;

            $fullnspath = mb_strtolower($namespace->fullcode);

            if ($fullnspath[0] !== '\\') {
                list($prefix) = explode('\\', $fullnspath, 1);
                $fullnspath = "\\$fullnspath";
            }

            if ($useType === 'class') {
                $this->calls->addCall(Calls::A_CLASS, $fullnspath, $namespace);
            }

            if ($this->nextIs(array($this->phptokens::T_AS))) {
                // use A\B as C
                $this->moveToNext();

                $fullnspath = makeFullNsPath($namespace->fullcode, $useType === 'const' ? \FNP_CONSTANT : \FNP_NOT_CONSTANT);
                $namespace->fullnspath = $fullnspath;

                $this->pushExpression($namespace);
                $as = $this->processAlias($useType);
                $as->fullnspath = makeFullNsPath($namespace->fullcode, $useType === 'const');
                $as->ws->totype = '';
                $as->rank = $rank;
                $fullcode[] = $as->fullcode;
                $as->alias = mb_strtolower(substr($as->fullcode, strrpos($as->fullcode, ' as ') + 4));

                $alias = $this->addNamespaceUse($origin, $as, $useType, $as);

                if (($use2 = $this->uses->get('class', $prefix)) instanceof AtomInterface) {
                    $this->addLink($as, $use2, 'DEFINITION');
                }
                $this->addLink($use, $as, 'USE');

                $namespace             = $as;
                $namespace->fullnspath = $fullnspath;
                $namespace->use        = $useType;
                $usesDefinitions[]     = $namespace;
                $this->runPlugins($namespace, array());

            } elseif ($this->nextIs(array($this->phptokens::T_NS_SEPARATOR))) {
                //use A\B\ {}
                $this->addLink($use, $namespace, 'GROUPUSE');
                $prefix = makeFullNsPath($namespace->fullcode);
                if ($prefix[0] !== '\\') {
                    $prefix = "\\$prefix";
                }
                $prefix .= '\\';
                $use->ws->toblock = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

                $this->moveToNext(); // Skip \

                $useTypeGeneric = $useType;
                do {
                    $this->moveToNext(); // Skip { or ,
                    // trailing comma
                    if ($this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
                        $use->trailing = self::TRAILING;
                        $last = count($use->ws->touseseparators) - 1;
                        $use->ws->touseseparators[$last] .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

                        continue;
                    }

                    $useType = $useTypeGeneric;
                    $totype = '';
                    if ($this->nextIs(array($this->phptokens::T_CONST))) {
                        // use const
                        $this->moveToNext();

                        $useType = 'const';
                        $totype = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                    }

                    if ($this->nextIs(array($this->phptokens::T_FUNCTION))) {
                        // use function
                        $this->moveToNext();

                        $useType = 'function';
                        $totype = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
                    }

                    $nsname = $this->processOneNsname();

                    if ($this->nextIs(array($this->phptokens::T_AS))) {
                        // A\B as C
                        $this->moveToNext();
                        $this->pushExpression($nsname);
                        $alias = $this->processAlias($useType);
                        $alias->ws->totype = $totype;

                        if ($useType === 'const') {
                            $nsname->fullnspath = $prefix . $nsname->fullcode;
                            $nsname->origin     = $prefix . $nsname->fullcode;

                            $alias->fullnspath  = $nsname->fullnspath;
                            $alias->origin      = $nsname->origin;
                        } else {
                            $nsname->fullnspath = $prefix . mb_strtolower($nsname->fullcode);
                            $nsname->origin     = $prefix . mb_strtolower($nsname->fullcode);

                            $alias->fullnspath  = $nsname->fullnspath;
                            $alias->origin      = $nsname->origin;
                        }

                        $aliasName = $this->addNamespaceUse($nsname, $alias, $useType, $alias);
                        $alias->alias = $aliasName;
                        $this->addLink($use, $alias, 'USE');
                        $usesDefinitions[] = $alias;

                    } else {
                        $this->addLink($use, $nsname, 'USE');
                        if ($useType === 'const') {
                            $nsname->fullnspath = $prefix . $nsname->fullcode;
                            $nsname->origin     = $prefix . $nsname->fullcode;
                        } else {
                            $nsname->fullnspath = $prefix . mb_strtolower($nsname->fullcode);
                            $nsname->origin     = $prefix . mb_strtolower($nsname->fullcode);
                        }

                        $alias = $this->addNamespaceUse($nsname, $nsname, $useType, $nsname);

                        $nsname->alias = $alias;
                        $usesDefinitions[] = $nsname;
                    }

                    $nsname->use = $useType;
                    $nsname->ws->totype = $totype;
                    $use->ws->touseseparators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

                } while ( $this->nextIs(array($this->phptokens::T_COMMA)));

                $fullcode[] = $namespace->fullcode . self::FULLCODE_BLOCK;

                $this->moveToNext(); // Skip }
            } else {
                $this->addLink($use, $namespace, 'USE');
                $namespace->rank = $rank;
                $usesDefinitions[] = $namespace;
                $namespace->use    = $useType;
                $namespace->ws->totype = '';

                $fullnspath = makeFullNsPath($namespace->fullcode, $useType === 'const' ? \FNP_CONSTANT : \FNP_NOT_CONSTANT);
                $namespace->fullnspath = $fullnspath;
                $namespace->origin     = $fullnspath;

                if (($use2 = $this->uses->get('class', $prefix)) instanceof AtomInterface) {
                    $this->addLink($namespace, $use2, 'DEFINITION');
                }

                $namespace->fullnspath = $fullnspath;
                $this->runPlugins($namespace, array());

                $alias = $this->addNamespaceUse($alias, $alias, $useType, $namespace);

                $namespace->alias = $alias;
                $origin->alias = $alias;

                $fullcode[] = $namespace->fullcode;
            }

            if ($this->nextIs(array($this->phptokens::T_COMMA))) {
                $use->ws->touseseparators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
            } else {
                $use->ws->touseseparators[] = '';
            }

        } while ($this->nextIs(array($this->phptokens::T_COMMA)));
        $this->runPlugins($use, $usesDefinitions);
        $use->count = $rank + 1; // final rank is the count total

        $use->fullcode = $this->tokens[$current][1] . ($useType !== 'class' ? ' ' . $useType : '') . ' ' . implode(', ', $fullcode);

        $this->pushExpression($use);

        $this->checkExpression();

        return $use;
    }

    private function processUseTrait(): AtomInterface {
        $current = $this->id;
        $use = $this->addAtom('Usetrait', $current);
        $use->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $fullcode = array();

        --$this->id;
        $extras = array();
        do {
            $this->moveToNext();
            $this->checkPhpdoc();
            $namespace = $this->processOneNsname(self::WITHOUT_FULLNSPATH);

            $fullcode[] = $namespace->fullcode;

            $this->getFullnspath($namespace, 'class', $namespace);

            $this->calls->addCall(Calls::A_CLASS, $namespace->fullnspath, $namespace);

            $this->addLink($use, $namespace, 'USE');
            $extras[] = $namespace;
            $this->checkPhpdoc();
            $use->ws->separators[] = $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];
        } while ($this->nextIs(array($this->phptokens::T_COMMA)));
        array_pop($use->ws->separators);
        $use->ws->separators[] = '';

        $fullcode = implode(', ', $fullcode);
        $this->runPlugins($use, $extras);

        if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
            //use A\B{} // Group
            $currentBlock = $this->id + 1;
            $block = $this->processUseBlock();
            $block->ws->opening = $this->tokens[$currentBlock][1] . $this->tokens[$currentBlock][4];
            $block->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->addLink($use, $block, 'BLOCK');
            $fullcode .= ' ' . $block->fullcode;

            // Several namespaces ? This has to be recalculated inside the block!!
            $namespace->fullnspath = makeFullNsPath($namespace->fullcode);
        }

        $use->fullcode = $this->tokens[$current][1] . ' ' . $fullcode;
        $this->pushExpression($use);

        return $use;
    }

    private function processUseBlock(): AtomInterface {
        $this->startSequence();

        // Case for {}
        $this->moveToNext();
        if ($this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
            $void = $this->addAtomVoid();
            $this->addToSequence($void);

            $this->moveToNext(); // skip }
        } else {
            $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
            do {
                $origin = $this->processOneNsname();
                $this->checkPhpdoc();
                if ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON))) {
                    $this->moveToNext(); // skip ::
                    $this->checkPhpdoc();
                    $method =  $this->processNextAsIdentifier();

                    $class = $origin;
                    $this->getFullnspath($class, 'class', $class);
                    $this->calls->addCall(Calls::A_CLASS, $class->fullnspath, $class);

                    $origin = $this->addAtom('Staticmethod', $this->id);
                    $this->addLink($origin, $class, 'CLASS');
                    $this->addLink($origin, $method, 'METHOD');

                    $origin->fullcode = "{$class->fullcode}::{$method->fullcode}";
                }
                $this->pushExpression($origin);

                $this->checkPhpdoc();
                $this->moveToNext();

                if ($this->nextIs(array($this->phptokens::T_AS), 0)) {
                    $this->processAsTrait();
                } elseif ($this->nextIs(array($this->phptokens::T_INSTEADOF), 0)) {
                    $this->processInsteadof();
                } else {
                    throw new UnknownCase('Usetrait without as or insteadof : ' . $this->tokens[$this->id + 1][1]);
                }

                $this->moveToNext();
                $this->processSemicolon(); // ;
                $this->checkPhpdoc();
            } while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY)));
            $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
            $this->moveToNext();
        }

        $this->checkExpression();

        $block = $this->sequence;
        $this->endSequence();

        $block->code     = '{}';
        $block->fullcode = static::FULLCODE_BLOCK;
        $block->bracket  = self::BRACKET;

        return $block;
    }

    private function processVariable(): AtomInterface {
        if ($this->tokens[$this->id][1] === '$this') {
            $atom = 'This';
        } elseif (in_array($this->tokens[$this->id][1], $this->PHP_SUPERGLOBALS, \STRICT_COMPARISON)) {
            $atom = 'Phpvariable';
        } elseif ($this->nextIs(array($this->phptokens::T_OBJECT_OPERATOR,
                                      $this->phptokens::T_NULLSAFE_OBJECT_OPERATOR,
                                      ))) {
            $atom = 'Variableobject';
        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET))) {
            $atom = 'Variablearray';
        } else {
            $atom = 'Variable';
        }
        $variable = $this->processSingle($atom);
        $this->pushExpression($variable);

        if ($atom === 'This' && ($class = $this->currentClassTrait->getCurrent())) {
            $variable->fullnspath = $class->fullnspath;
            $this->calls->addCall(Calls::A_CLASS, $class->fullnspath, $variable);
        }
        $this->runPlugins($variable);
        $variable->ws->opening = $this->tokens[$this->id][1];

        if (in_array($atom, array('Variable', 'Variableobject', 'Variablearray'), \STRICT_COMPARISON) &&
            $this->currentReturn !== null) {
            $this->addLink($this->currentReturn, $variable, 'RETURNED');
        }

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
             $variable = $this->processFCOA($variable);
        }

        return $variable;
    }

    private function processFCOA(AtomInterface $nsname): AtomInterface {
        // for functions
        if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            return $this->processFunctioncall();
        }

        // for $a++
        if ($this->nextIs(array($this->phptokens::T_INC,
                                $this->phptokens::T_DEC))) {
            return $this->processPostPlusplus($nsname);
        }

        // for array appends
        if ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET)) &&
            $this->nextIs(array($this->phptokens::T_CLOSE_BRACKET), 2)) {
            return $this->processAppend();
        }

        // for arrays
        if ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET)) ||
            $this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {

            if ($nsname->isA(array('Nsname', 'Identifier'))) {
                $type = $this->contexts->isContext(Context::CONTEXT_NEW) ? 'class' : 'const';
                if ($type === 'const') {
                    $this->getFullnspath($nsname, $type, $nsname);
                    $this->runPlugins($nsname);
                    $this->calls->addCall(Calls::CONST, $nsname->fullnspath, $nsname);
                }
            }

            return $this->processBracket();
        }

        // for simple identifiers
        if ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON)) ||
            $this->nextIs(array($this->phptokens::T_NS_SEPARATOR)) ||
            $this->nextIs(array($this->phptokens::T_INSTANCEOF, -1))   ||
            $this->nextIs(array($this->phptokens::T_AS), -1)) {
            return $nsname;
        }

        if ($nsname->atom === 'Newcall') {
            // New call, but no () : it still requires an argument count
            $nsname->count = $nsname->count ?? 0 ;

            return $nsname;
        }

        if ($nsname->isA(array('Nsname', 'Identifier'))) {
            $type = $this->contexts->isContext(Context::CONTEXT_NEW) ? 'class' : 'const';
            $this->getFullnspath($nsname, $type, $nsname);

            if ($type === 'const') {
                $this->runPlugins($nsname);
                $this->calls->addCall(Calls::CONST, $nsname->fullnspath, $nsname);
            }
        }

        return $nsname;
    }

    private function processAppend(): AtomInterface {
        $current = $this->id;
        $append = $this->addAtom('Arrayappend', $current);

        $left = $this->popExpression();
        $this->addLink($append, $left, 'APPEND');

        $append->fullcode = $left->fullcode . '[]';
        $append->ws->closing = '[' . $this->tokens[$current + 1][4] . ']' . $this->tokens[$current + 2][4];

        $this->pushExpression($append);
        $this->runPlugins($append, array('APPEND' => $left));

        $this->moveToNext();
        $this->moveToNext();

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            // Mostly for arrays
            $append = $this->processFCOA($append);
        }

        return $append;
    }

    private function processInteger(): AtomInterface {
        $integer = $this->addAtom('Integer', $this->id);

        $integer->fullcode = $this->tokens[$this->id][1];

        $this->pushExpression($integer);
        $this->runPlugins($integer);
        $this->checkExpression();

        return $integer;
    }

    private function processFloat(): AtomInterface {
        $float = $this->addAtom('Float', $this->id);

        $float->fullcode = $this->tokens[$this->id][1];

        $this->pushExpression($float);
        // (int) is for loading into the database
        $this->runPlugins($float);

        $this->checkExpression();

        return $float;
    }

    private function processLiteral(): AtomInterface {
        $literal = $this->processSingle('String');
        $this->pushExpression($literal);

        if ($this->nextIs(array($this->phptokens::T_CONSTANT_ENCAPSED_STRING), 0)) {
            $literal->delimiter   = $literal->code[0];
            if ($literal->delimiter === 'b' || $literal->delimiter === 'B') {
                $literal->binaryString = $literal->delimiter;
                $literal->delimiter    = $literal->code[1];
                $literal->noDelimiter  = substr($literal->code, 2, -1);
            } else {
                $literal->noDelimiter = substr($literal->code, 1, -1);
            }

            if (in_array(mb_strtolower($literal->noDelimiter),  array('parent', 'self', 'static'), \STRICT_COMPARISON)) {
                $this->getFullnspath($literal, 'class', $literal);

                $this->calls->addCall(Calls::A_CLASS, $literal->fullnspath, $literal);
            } else {
                $this->calls->addNoDelimiterCall($literal);
            }
        } elseif ($this->nextIs(array($this->phptokens::T_NUM_STRING), 0)) {
            $literal->delimiter   = '';
            $literal->noDelimiter = $literal->code;

            $this->calls->addNoDelimiterCall($literal);
        } else {
            $literal->delimiter   = '';
            $literal->noDelimiter = '';
        }
        $this->runPlugins($literal);
        if ($this->nextIs(array($this->phptokens::T_OPEN_BRACKET))) {
            $literal = $this->processBracket();
        }

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            $literal = $this->processFCOA($literal);
        }

        $literal->ws->opening = '';

        return $literal;
    }

    private function processMagicConstant(): AtomInterface {
        $constant = $this->processSingle('Magicconstant');
        $this->pushExpression($constant);

        if (mb_strtolower($constant->fullcode) === '__dir__') {
            $path = dirname($this->filename);
            $constant->noDelimiter = $path === '/' ? '' : $path;
        } elseif (mb_strtolower($constant->fullcode) === '__file__') {
            $constant->noDelimiter = $this->filename;
        } elseif (mb_strtolower($constant->fullcode) === '__function__') {
            if (empty($this->currentFunction)) {
                $constant->noDelimiter = '';
            } else {
                $constant->noDelimiter = $this->currentFunction[count($this->currentFunction) - 1]->code;
            }
        } elseif (mb_strtolower($constant->fullcode) === '__class__') {
            if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                $constant->noDelimiter = '';
            } elseif ($this->currentClassTrait->getCurrent()->atom === 'Class') {
                $constant->noDelimiter = $this->currentClassTrait->getCurrent()->fullnspath;
            } else {
                $constant->noDelimiter = '';
            }
        } elseif (mb_strtolower($constant->fullcode) === '__trait__') {
            if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                $constant->noDelimiter = '';
            } elseif ($this->currentClassTrait->getCurrent()->atom === 'Trait') {
                $constant->noDelimiter = $this->currentClassTrait->getCurrent()->fullnspath;
            } else {
                $constant->noDelimiter = '';
            }
        } elseif (mb_strtolower($constant->fullcode) === '__line__') {
            $constant->noDelimiter = $this->tokens[$this->id][2];
        } elseif (mb_strtolower($constant->fullcode) === '__method__') {
            if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                if (count($this->currentMethod) === 1) {
                    $constant->noDelimiter = '';
                } else {
                    $constant->noDelimiter = $this->currentMethod[count($this->currentMethod) - 1]->code;
                }
            } elseif (count($this->currentMethod) === 1) {
                $constant->noDelimiter = '';
            } elseif ($this->currentClassTrait->getCurrent() !== ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                $constant->noDelimiter = $this->currentClassTrait->getCurrent()->fullnspath .
                                         '::' .
                                         $this->currentClassTrait->getCurrent()->code;
            } else {
                $constant->noDelimiter = '';
            }
        }

        $constant->intval  = (int) $constant->noDelimiter;
        $constant->boolean = (bool) $constant->intval;
        $this->runPlugins($constant);

        $constant = $this->processFCOA($constant);

        return $constant;
    }

    //////////////////////////////////////////////////////
    /// processing single operators
    //////////////////////////////////////////////////////
    private function processSingleOperator(AtomInterface $operator, array $finals = array(), string $link = '', string $separator = ''): AtomInterface {
        assert($link !== '', 'Link cannot be empty');

        $current = $this->id;

        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        // Do while, so that AT least one loop is done.
        do {
            $operand = $this->processNext();
        } while (!$this->nextIs($finals));
        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

        $this->popExpression();
        $this->addLink($operator, $operand, $link);

        $operator->fullcode = $this->tokens[$current][1] . $separator . $operand->fullcode;

        $this->runPlugins($operator, array($link => $operand));
        $this->pushExpression($operator);

        $this->checkExpression();

        return $operand;
    }

    private function processCast(): AtomInterface {
        $operator = $this->addAtom('Cast', $this->id);
        $operator->ws->opening  = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] ;

        $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$this->id][0]), 'CAST', ' ');
        $this->popExpression();
        if (strtolower($operator->code) === '(binary)') {
            $operator->binaryString = $operator->code[1];
        }
        $this->pushExpression($operator);

        return $operator;
    }

    private function processReturn(): AtomInterface {
        $current = $this->id;
        // Case of return ;
        $return = $this->addAtom('Return', $current);
        $this->makePhpdoc($return);
        $return->ws->opening  = $this->tokens[$current][1] . $this->tokens[$current][4] ;

        if ($this->nextis(array($this->phptokens::T_CLOSE_TAG,
                                $this->phptokens::T_SEMICOLON))) {


            $returnArg = $this->addAtomVoid();
            $returnArg->ws->closing = '';
            $this->addLink($return, $returnArg, 'RETURN');

            $return->fullcode = $this->tokens[$current][1] . ' ;';

            $this->runPlugins($return, array('RETURN' => $returnArg) );

            $this->pushExpression($return);
            if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
                $this->processSemicolon();
            }

            if (!empty($this->currentMethod) !== null) {
                $this->addLink($this->currentMethod[count($this->currentMethod) - 1], $returnArg, 'RETURNED');
            }

            return $return;
        }

        if (!empty($this->currentMethod)) {
            $this->currentReturn = $this->currentMethod[count($this->currentMethod) - 1];
        }

        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        $finals =  $this->precedence->get($this->tokens[$this->id][0]);
        do {
            $returned = $this->processNext();
        } while (!$this->nextIs($finals));
        $this->popExpression();

        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

        $this->addLink($return, $returned, 'RETURN');

        $return->fullcode     = $this->tokens[$current][1] . ' ' . $returned->fullcode;

        // raw variables are done
        if (!$returned->isA(array('Variable', 'Variableobject', 'Variablearray')) &&
            $this->currentReturn !== null) {
            $this->addLink($this->currentReturn, $returned, 'RETURNED');
       }
        $this->currentReturn = null;

       $this->runPlugins($return, array('RETURN' => $returned) );

       $this->pushExpression($return);
       $this->checkExpression();

        return $return;
    }

    private function processThrow(): AtomInterface {
        $operator = $this->addAtom('Throw', $this->id);
        $operator->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$this->id][0]), 'THROW', ' ');
        $operator = $this->popExpression();
        $this->pushExpression($operator);

        $this->checkExpression();

        return $operator;
    }

    private function makeAttributes(AtomInterface $node): array {
        foreach($this->attributes as $attribute) {
            $this->addLink($node, $attribute, 'ATTRIBUTE');
        }

        $return = $this->attributes;
        $this->attributes = array();

        return $return;
    }

    private function processYield(): AtomInterface {
        if ($this->nextIs($this->END_OF_EXPRESSION)) {
            $current = $this->id;

            // Case of return ;
            $yieldArg = $this->addAtomVoid();
            $yield = $this->addAtom('Yield', $current);

            $this->addLink($yield, $yieldArg, 'YIELD');

            $yield->fullcode = $this->tokens[$current][1] . ' ;';
            $yield->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->pushExpression($yield);
            $this->runPlugins($yield, array('YIELD' => $yieldArg) );

            if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
                $this->processSemicolon();
            }

            return $yield;
        } else {
            // => is actually a lower priority
            $finals = $this->precedence->get($this->tokens[$this->id][0]);
            $id = array_search($this->phptokens::T_DOUBLE_ARROW, $finals);
            unset($finals[$id]);
            $operator = $this->addAtom('Yield', $this->id);
            $operator->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            $this->processSingleOperator($operator, $finals, 'YIELD', ' ');

            return $operator;
        }
    }

    private function processYieldfrom(): AtomInterface {
        $operator = $this->addAtom('Yieldfrom', $this->id);
        $operator->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$this->id][0]), 'YIELD', ' ');

        $this->checkExpression();

        return $operator;
    }

    private function processNot(): AtomInterface {
        $current = $this->id;
        $finals = array_diff($this->precedence->get($this->tokens[$this->id][0]),
                             $this->assignations
                             );
        $operator = $this->addAtom('Not', $this->id);
        $operator->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $this->processSingleOperator($operator, $finals, 'NOT');

        $this->checkExpression();

        return $operator;
    }

    private function processCurlyExpression(): AtomInterface {
        $current = $this->id;
        $this->moveToNext();
        do {
            $code = $this->processNext();
        } while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY)));
        $this->popExpression();

        $block = $this->addAtom('Block', $this->id);
        $block->code     = '{}';
        $block->fullcode = '{' . $code->fullcode . '}';
        $block->ws->opening = $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];

        $this->addLink($block, $code, 'CODE');

        $this->runPlugins($block, array('CODE' => $code));

        $this->moveToNext(); // Skip }
        $block->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        return $block;
    }

    private function processDollar(): AtomInterface {
        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
            $current = $this->id;

            $variable = $this->addAtom('Variable', $current);
            $variable->token = 'T_DOLLAR_OPEN_CURLY_BRACES';

            $this->moveToNext();
            while (!$this->nextIs(array($this->phptokens::T_CLOSE_CURLY))) {
                $this->processNext();
            }

            // Skip }
            $this->moveToNext();

            $expression = $this->popExpression();
            $this->addLink($variable, $expression, 'NAME');

            $variable->fullcode = $this->tokens[$current][1] . '{' . $expression->fullcode . '}';
            $this->runPlugins($variable, array('NAME' => $expression));
            $this->pushExpression($variable);

            $variable->ws->opening  = $this->tokens[$current][1] . $this->tokens[$current][4] .
                                      $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
            $variable->ws->closing  = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

            if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
                $this->processSemicolon();
            } elseif (!in_array($this->tokens[$current - 1][0], array($this->phptokens::T_OBJECT_OPERATOR,
                                                                      $this->phptokens::T_NULLSAFE_OBJECT_OPERATOR,
                                                                      $this->phptokens::T_DOUBLE_COLON,
                                                                      ),
                        \STRICT_COMPARISON)) {
                $variable = $this->processFCOA($variable);
            }

        } else {
            $operator = $this->addAtom('Variable', $this->id);
            $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$this->id][0]), 'NAME');
            $variable = $this->popExpression();
            $variable->ws->opening  = $this->tokens[$current][1] . $this->tokens[$current][4];
            $variable->ws->closing  = '';

            $this->pushExpression($variable);
        }

        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
        $this->checkExpression();

        return $variable;
    }

    private function processClone(): AtomInterface {
        $operator = $this->addAtom('Clone', $this->id);
        $operator->ws->opening  = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] ;
        $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$this->id][0]), 'CLONE', ' ' );
        $operatorId = $this->popExpression();
        $this->pushExpression($operatorId);

        return $operatorId;
    }

    private function processGoto(): AtomInterface {
        $current = $this->id;

        $label = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);

        $goto = $this->addAtom('Goto', $current);
        $goto->fullcode  = $this->tokens[$current][1] . ' ' . $label->fullcode;
        $goto->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->addLink($goto, $label, 'GOTO');

        if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
            $class = '\global';
        } else {
            $class = $this->currentClassTrait->getCurrent()->fullnspath;
        }

        if (empty($this->currentFunction)) {
            $method = '';
        } else {
            $method = end($this->currentFunction)->fullnspath;
        }

        $this->runPlugins($goto, array('GOTO' => $label));
        $this->calls->addCall(Calls::GOTO, $class . '::' . $method . '..' . $this->tokens[$this->id][1], $goto);
        $this->pushExpression($goto);

        return $goto;
    }

    private function processNoscream(): AtomInterface {
        $current = $this->id;
        $atom = $this->processExpression($this->precedence->get($this->tokens[$this->id][0]));
        $atom->noscream = self::NOSCREAM;
        $atom->ws->noscream = $this->tokens[$current][1] . $this->tokens[$current][4];
        $atom->fullcode = "@{$atom->fullcode}";
        $this->pushExpression($atom);

        $this->checkExpression();

        return $atom;
    }

    private function processNew(): AtomInterface {
        $current = $this->id;

        $this->checkAttribute();

        $this->contexts->toggleContext(Context::CONTEXT_NEW);
        $noSequence = $this->contexts->isContext(Context::CONTEXT_NOSEQUENCE);
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }

        $operator = $this->addAtom('New', $current);
        $operator->fullcode = $this->tokens[$current][1];
        $operator->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $newcall = $this->processSingleOperator($operator, $this->precedence->get($this->tokens[$current][0]), 'NEW', ' ');
//        $this->runPlugins($newcall, array());

        $this->contexts->toggleContext(Context::CONTEXT_NEW);
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }

        $operator = $this->popExpression();
        $this->pushExpression($operator);

        $this->checkExpression();

        return $operator;
    }

    //////////////////////////////////////////////////////
    /// processing binary operators
    //////////////////////////////////////////////////////
    private function processSign(): AtomInterface {
        $current = $this->id;
        $signExpression = $this->tokens[$this->id][1];
        $whitespaces    = array($this->tokens[$this->id][1] . $this->tokens[$this->id][4]);
        while ($this->nextIs(array($this->phptokens::T_PLUS,
                                   $this->phptokens::T_MINUS))) {
            $this->moveToNext();
            $signExpression = $this->tokens[$this->id][1] . $signExpression;
            $whitespaces[] = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
        }

        if (($this->nextIs(array($this->phptokens::T_LNUMBER)) ||
             $this->nextIs(array($this->phptokens::T_DNUMBER))) &&
             !$this->nextIs(array($this->phptokens::T_POW), 2)) {
            $operand = $this->processNext();

            $operand->code     = $signExpression . $operand->code;
            $operand->fullcode = $signExpression . $operand->fullcode;
            $operand->token    = $this->getToken($this->tokens[$this->id][0]);
            $this->runPlugins($operand);

            return $operand;
        }

        $finals = $this->precedence->get($this->tokens[$this->id][0]);
        $finals[] = '-';
        $finals[] = '+';

        $noSequence = $this->contexts->isContext(Context::CONTEXT_NOSEQUENCE);
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }
        do {
            $this->processNext();
        } while (!$this->nextIs($finals));
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }
        $signed = $this->popExpression();
        $firstSigned = $signed;

        for($i = strlen($signExpression) - 1; $i >= 0; --$i) {
            $sign = $this->addAtom('Sign', $current);
            $this->addLink($sign, $signed, 'SIGN');

            $sign->code     = $signExpression[$i];
            $sign->fullcode = $signExpression[$i] . $signed->fullcode;

            $signed = $sign;
        }
        $signed->ws->opening = implode('', $whitespaces);
        $this->runPlugins($sign, array('SIGN' => $firstSigned));

        $this->pushExpression($signed);

        $this->checkExpression();
        return $signed;
    }

    private function processAddition(): AtomInterface {
        if (!$this->hasExpression() ||
            $this->tokens[$this->id - 1][0] === $this->phptokens::T_DOT
            ) {
            return $this->processSign();
        }

        $finals = $this->precedence->get($this->tokens[$this->id][0], Precedence::WITH_SELF);
        $finals = array_diff($finals, $this->assignations);
        $finals = array_unique($finals);

        return $this->processOperator('Addition', $finals, array('LEFT', 'RIGHT'));
    }

    private function processBreak(): AtomInterface {
        $current = $this->id;
        $break = $this->addAtom($this->nextIs(array($this->phptokens::T_BREAK), 0) ? 'Break' : 'Continue', $current);

        if ($this->nextIs(array($this->phptokens::T_LNUMBER))) {
            $noSequence = $this->contexts->isContext(Context::CONTEXT_NOSEQUENCE);
            if ($noSequence === false) {
                $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
            }

            $this->moveToNext();
            $breakLevel = $this->processInteger();
            $this->popExpression();

            if ($noSequence === false) {
                $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
            }

        } elseif ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            $this->moveToNext(); // skip (
            $this->processNext();
            $this->moveToNext(); // skip )

            $breakLevel = $this->popExpression();
        } elseif ($this->nextIs(array($this->phptokens::T_CLOSE_TAG)) ||
                  $this->nextIs(array($this->phptokens::T_SEMICOLON ))) {
            $breakLevel = $this->addAtomVoid();
        } else {
            $this->processNext();

            $breakLevel = $this->popExpression();
        }

        $link = $this->tokens[$current][0] === $this->phptokens::T_BREAK ? 'BREAK' : 'CONTINUE';
        $this->addLink($break, $breakLevel, $link);
        $break->fullcode = $this->tokens[$current][1] . ( $breakLevel->atom !== 'Void' ? ' ' . $breakLevel->fullcode : '');
        $break->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->runPlugins($break, array($link => $breakLevel));
        $this->pushExpression($break);

        $this->checkExpression();

        return $break;
    }

    private function processDoubleColon(): AtomInterface {
        $current = $this->id;

        $left = $this->popExpression();

        $this->checkPhpdoc();

        $this->contexts->nestContext(Context::CONTEXT_NEW);
        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
            $right = $this->processCurlyExpression();
        } elseif ($this->nextIs(array($this->phptokens::T_DOLLAR))) {
            $this->moveToNext(); // Skip ::
            $right = $this->processDollar();
            $this->popExpression();
        } elseif ($this->nextIs(array($this->phptokens::T_CLASS))) {
            if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS), 2)) {
                $this->moveToNext();
                $right = $this->processSingle('Name');
            } else {
                $right = $this->tokens[$this->id + 1][1];
                $this->moveToNext(); // Skip ::
            }
        } elseif ($this->nextIs(array($this->phptokens::T_VARIABLE))) {
            $this->moveToNext();
            $right = $this->processSingle('Staticpropertyname');
        } else {
            $right = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        }

        if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            $this->pushExpression($right);
            $right = $this->processFunctioncall(self::WITHOUT_FULLNSPATH);
            $this->popExpression();
        }

        $this->contexts->exitContext(Context::CONTEXT_NEW);
        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

        // @todo : remove $right is a string (cf L 6568, to make $right always an object)
        if (is_string($right) && mb_strtolower($right) === 'class') {
            $static = $this->addAtom('Staticclass', $current);
            $fullcode = "$left->fullcode::$right";
            $static->ws->closing = $this->tokens[$current][1] . $this->tokens[$current][4] . $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];

            if (!$left->isA(array('Functioncall', 'Methodcall', 'Staticmethodcall'))) {
                $this->getFullnspath($left, 'class', $left);
                $this->calls->addCall(Calls::A_CLASS, $left->fullnspath, $left);
            }
            // We are not sending $left, as it has no impact
            $this->runPlugins($left);
            $this->runPlugins($static, array('CLASS' => $left));
            // This should actually be the value of any USE statement
            if (($use = $this->uses->get('class', mb_strtolower($left->fullcode))) instanceof AtomInterface) {
                $noDelimiter = $use->fullcode;
                if (($length = strpos($noDelimiter, ' ')) !== false) {
                    $noDelimiter = substr($noDelimiter, 0, $length);
                }
                $static->noDelimiter = $noDelimiter;
            } else {
                $static->noDelimiter = $left->fullcode;
            }
        } elseif ($right->isA(array('Name'))) {
            $static = $this->addAtom('Staticconstant', $current);
            $this->addLink($static, $right, 'CONSTANT');
            $fullcode = "{$left->fullcode}::{$right->fullcode}";

            if ($left->isA(array('Identifier', 'Nsname', 'Parent', 'Static', 'Self'))) {
                $this->getFullnspath($left, 'class', $left);
                $this->calls->addCall(Calls::A_CLASS, $left->fullnspath, $left);

                $static->fullnspath = "{$left->fullnspath}::{$right->fullcode}";
            }
            $this->runPlugins($static, array('CLASS'    => $left,
                                             'CONSTANT' => $right));
        } elseif ($right->isA(array('Variable',
                                    'Array',
                                    'Arrayappend',
                                    'MagicConstant',
                                    'Concatenation',
                                    'Block',
                                    'Boolean',
                                    'Null',
                                    'Staticpropertyname',
                                    ))) {
            $static = $this->addAtom('Staticproperty', $current);

            if ($left->isA(array('Identifier', 'Nsname', 'Parent', 'Static', 'Self'))) {
                $this->getFullnspath($left, 'class', $left);
                $this->calls->addCall(Calls::A_CLASS, $left->fullnspath, $left);

                $static->fullnspath = "{$left->fullnspath}::{$right->fullcode}";
            }
            $this->addLink($static, $right, 'MEMBER');
            $fullcode = "{$left->fullcode}::{$right->fullcode}";

            $this->runPlugins($static, array('CLASS'  => $left,
                                             'MEMBER' => $right));
        } elseif ($right->isA(array('Methodcallname', 'Callable'))) {
            if ($right->isA(array('Callable'))) {
                $static = $this->addAtom('Callable', $current);
            } else {
                $static = $this->addAtom('Staticmethodcall', $current);
            }
            $this->addLink($static, $right, 'METHOD');

            if ($left->isA(array('Identifier', 'Nsname', 'Parent', 'Static', 'Self'))) {
                $this->getFullnspath($left, 'class', $left);
                $this->calls->addCall(Calls::A_CLASS, $left->fullnspath, $left);

                $static->fullnspath = $left->fullnspath . '::' . mb_strtolower($right->code);
            }
            $fullcode = "{$left->fullcode}::{$right->fullcode}";
            $this->runPlugins($static, array('CLASS'  => $left,
                                             'METHOD' => $right));
        } else {
            throw new LoadError('Unprocessed atom in static call (right) : ' . $right->atom . ':' . $this->filename . ':' . __LINE__);
        }
        $this->makePhpdoc($static);

        $this->addLink($static, $left, 'CLASS');
        if ($static->atom  === 'Staticproperty'                                                  &&
            in_array($left->token, array('T_STRING', 'T_STATIC'), \STRICT_COMPARISON)            &&
            $this->currentClassTrait->getCurrent() !== ClassTraitContext::NO_CLASS_TRAIT_CONTEXT &&
            $left->fullnspath === $this->currentClassTrait->getCurrent()->fullnspath) {

            $name = ltrim($right->code, '$');
            if (!empty($name)) {
                array_collect_by($this->currentPropertiesCalls, $name, $static);
            }
        }

        if ($static->atom  === 'Staticmethodcall'                                                &&
            in_array($left->token, array('T_STRING', 'T_STATIC'), \STRICT_COMPARISON)            &&
            $this->currentClassTrait->getCurrent() !== ClassTraitContext::NO_CLASS_TRAIT_CONTEXT &&
            $left->fullnspath === $this->currentClassTrait->getCurrent()->fullnspath) {
                array_collect_by($this->currentMethodsCalls, mb_strtolower($right->code), $static);
        }

        $static->fullcode = $fullcode;
        $static->ws->opening  = '';
        $static->ws->operator = '::' . $this->tokens[$current][4];

        if (!empty($left->fullnspath)){
            if ($static->isA(array('Staticmethodcall', 'Staticmethod'))) {
                $name = mb_strtolower($right->code);
                $this->calls->addCall(Calls::STATICMETHOD,  "$left->fullnspath::$name", $static);
            } elseif ($static->atom === 'Staticconstant') {
                $this->calls->addCall(Calls::STATICCONSTANT,  "$left->fullnspath::$right->code", $static);
            } elseif ($static->atom === 'Staticproperty' && ($right->token === 'T_VARIABLE')) {
                $this->calls->addCall(Calls::STATICPROPERTY, "$left->fullnspath::$right->code", $static);
            }
        }

        $this->pushExpression($static);

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            $static = $this->processFCOA($static);
        }

        return $static;
    }

    private function processOperator(string $atom, array $finals, array $links = array('LEFT', 'RIGHT')): AtomInterface {
        $current = $this->id;
        $operator = $this->addAtom($atom, $current);
        $this->makePhpdoc($operator);

        $left = $this->popExpression();
        $this->addLink($operator, $left, $links[0]);

        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
//        $this->checkPhpdoc();
        do {
            $right = $this->processNext();

            if ($this->nextIs($this->assignations)) {
                $right = $this->processNext();
            }
            $this->checkPhpdoc();
        } while (!$this->nextIs($finals) );

        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);
        $this->popExpression();

        $this->addLink($operator, $right, $links[1]);

        // This adds DEFAULT to local variables.
        if ($operator->code === '='        &&
            $left->atom     === 'Variable' &&
            $this->currentVariables->exists($left->code)) {
            $this->addLink($this->currentVariables->get($left->code), $right, 'DEFAULT');
        }

        $operator->fullcode  = $left->fullcode . ' ' . $this->tokens[$current][1] . ' ' . $right->fullcode;
        $operator->ws->operator = $this->tokens[$current][1] . $this->tokens[$current][4];

        $extras = array($links[0] => $left, $links[1] => $right);
        $this->runPlugins($operator, $extras);

        $this->pushExpression($operator);
        $this->checkExpression();

        return $operator;
    }

    private function processObjectOperator(): AtomInterface {
        $current = $this->id;

        $left = $this->popExpression();
        if ($this->currentVariables->exists($left->code)) {
            $cv = $this->currentVariables->get($left->code);
            $left->isPhp  = $cv->isPhp;
            $left->isExt  = $cv->isExt;
            $left->isStub = $cv->isStub;
            $left->fullnspath = $cv->fullnspath;
        }
        $this->checkPhpdoc();

        $this->contexts->nestContext(Context::CONTEXT_NEW);
        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        if ($this->nextIs(array($this->phptokens::T_OPEN_CURLY))) {
            $right = $this->processCurlyExpression();
        } elseif ($this->nextIs(array($this->phptokens::T_VARIABLE))) {
            $this->moveToNext();
            $right = $this->processSingle('Variable');
            $right->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];
            $right->ws->closing = '';
        } elseif ($this->nextIs(array($this->phptokens::T_DOLLAR))) {
            $this->moveToNext();
            $right = $this->processDollar();
            $this->popExpression();
        } else {
            $right = $this->processNextAsIdentifier(self::WITHOUT_FULLNSPATH);
        }

        if ($this->nextIs(array($this->phptokens::T_OPEN_PARENTHESIS))) {
            $this->pushExpression($right);
            $right = $this->processFunctioncall(self::WITHOUT_FULLNSPATH);
            $this->popExpression();
        }

        $this->contexts->exitContext(Context::CONTEXT_NEW);
        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

        if ($right->isA(array('Variable',
                              'Array',
                              'Name',
                              'Concatenation',
                              'Arrayappend',
                              'Member',
                              'MagicConstant',
                              'Block',
                              'Boolean',
                              'Null',
                              ))) {
            $static = $this->addAtom('Member', $current);
            $links = 'MEMBER';
            $static->enclosing = self::NO_ENCLOSING;
        } elseif ($right->isA(array('Closure'))) {
            $static = $this->addAtom('Closure', $current);
            $links = 'METHOD';
        } elseif ($right->isA(array('Callable'))) {
            $static = $this->addAtom('Callable', $current);
            $links = 'METHOD';
        } elseif ($right->isA(array('Methodcallname', 'Methodcall'))) {
            $static = $this->addAtom('Methodcall', $current);
            $links = 'METHOD';
        } else {
            throw new LoadError('Unprocessed atom in object call (right) : ' . $right->atom . ':' . $this->filename . ':' . __LINE__);
        }

        $this->addLink($static, $left, 'OBJECT');
        $this->addLink($static, $right, $links);

        $static->fullcode     = $left->fullcode . $this->tokens[$current][1] . $right->fullcode;
        $static->ws->opening  = '';
        $static->ws->operator = $this->tokens[$current][1] . $this->tokens[$current][4];

        if ($left->atom === 'This' ){
            if ($static->atom === 'Methodcall') {
                $this->calls->addCall(Calls::METHOD, $left->fullnspath . '::' . mb_strtolower($right->code), $static);
                array_collect_by($this->currentMethodsCalls, mb_strtolower($right->code), $static);
            } elseif ($static->atom  === 'Member'   &&
                      $right->token  === 'T_STRING') {

                $this->calls->addCall(Calls::PROPERTY, "{$left->fullnspath}::{$right->code}", $static);
                array_collect_by($this->currentPropertiesCalls, $right->code, $static);
            }
        }
        $this->runPlugins($static, array('OBJECT' => $left,
                                         $links   => $right,
                                         ));
        $this->pushExpression($static);

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        } else {
            $static = $this->processFCOA($static);
        }

        return $static;
    }

    private function processAssignation(): AtomInterface {
        $finals = $this->precedence->get($this->tokens[$this->id][0]);
        $finals = array_merge($finals, $this->assignations);

        return $this->processOperator('Assignation', $finals);
    }

    private function processCoalesce(): AtomInterface {
        return $this->processOperator('Coalesce', $this->precedence->get($this->tokens[$this->id][0], Precedence::WITH_SELF));
    }

    private function processEllipsis(): AtomInterface {
        $current = $this->id;
        // Simply skipping the ...
        $finals = $this->precedence->get($this->phptokens::T_ELLIPSIS);
        do {
            $operand = $this->processNext();
        } while (!$this->nextIs($finals));

        $this->popExpression();
        $operand->fullcode     = '...' . $operand->fullcode;
        $operand->variadic     = self::VARIADIC;
        $operand->ws->ellipsis =  $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->pushExpression($operand);

        return $operand;
    }

    private function processAnd(): AtomInterface {
        if ($this->hasExpression()) {
            return $this->processOperator('Bitoperation', $this->precedence->get($this->tokens[$this->id][0]));
        }

        $current = $this->id;
        // Simply skipping the &
        do {
            $operand = $this->processNext();
        } while ($this->nextIs(array($this->phptokens::T_DOUBLE_COLON,
                                     $this->phptokens::T_OBJECT_OPERATOR,
                                     $this->phptokens::T_NULLSAFE_OBJECT_OPERATOR,
                                     )));

        $this->popExpression();
        $operand->fullcode  = '&' . $operand->fullcode;
        $operand->reference = self::REFERENCE;
        $operand->ws->reference = '&' . $this->tokens[$current][4];

        $this->pushExpression($operand);

        return $operand;
    }

    private function processLogical(): AtomInterface {
        return $this->processOperator('Logical', $this->precedence->get($this->tokens[$this->id][0]));
    }

    private function processBitoperation(): AtomInterface {
        return $this->processOperator('Bitoperation', $this->precedence->get($this->tokens[$this->id][0]));
    }

    private function processMultiplication(): AtomInterface {
        return $this->processOperator('Multiplication', $this->precedence->get($this->tokens[$this->id][0], Precedence::WITH_SELF));
    }

    private function processPower(): AtomInterface {
        return $this->processOperator('Power', $this->precedence->get($this->tokens[$this->id][0], Precedence::WITH_SELF));
    }

    private function processSpaceship(): AtomInterface {
        return $this->processOperator('Spaceship', $this->precedence->get($this->tokens[$this->id][0]));
    }

    private function processComparison(): AtomInterface {
        return $this->processOperator('Comparison', $this->precedence->get($this->tokens[$this->id][0]));
    }

    private function processDot(): AtomInterface {
        $concatenation = $this->addAtom('Concatenation', $this->id);
        $fullcode      = array();
        $concat        = array();
        $noDelimiter   = '';
        $rank          = -1;

        $contains       = $this->popExpression();
        $contains->rank = ++$rank;
        $fullcode[]     = $contains->fullcode;
        $concat[]       = $contains;
        $noDelimiter   .= $contains->noDelimiter;
        $this->addLink($concatenation, $contains, 'CONCAT');
        $concatenation->ws->opening = '';
        $concatenation->ws->separators[] = '.' . $this->tokens[$this->id][4];
        $concatenation->ws->closing = '';

        $this->contexts->nestContext(Context::CONTEXT_NOSEQUENCE);
        $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);

        $finals = $this->precedence->get($this->tokens[$this->id][0]);
        $finals = array_diff($finals, array($this->phptokens::T_REQUIRE,
                                            $this->phptokens::T_REQUIRE_ONCE,
                                            $this->phptokens::T_INCLUDE,
                                            $this->phptokens::T_INCLUDE_ONCE,
                                            $this->phptokens::T_PRINT,
                                            $this->phptokens::T_ECHO,
                                            $this->phptokens::T_YIELD,
                                            $this->phptokens::T_YIELD_FROM,
                                            // This is for 'a' . -$y
                                            $this->phptokens::T_PLUS,
                                            $this->phptokens::T_MINUS,
                                            ));

        while (!$this->nextIs($finals)) {
            $contains = $this->processNext();

            if ($this->nextIs(array($this->phptokens::T_DOT))) {
                $this->popExpression();
                $this->addLink($concatenation, $contains, 'CONCAT');
                $fullcode[]     = $contains->fullcode;
                $concat[]       = $contains;
                $noDelimiter   .= $contains->noDelimiter;
                $contains->rank = ++$rank;

                $this->moveToNext();
                $concatenation->ws->separators[] = '.' . $this->tokens[$this->id][4];
            }
        }

        $this->contexts->exitContext(Context::CONTEXT_NOSEQUENCE);

        $this->popExpression();
        $this->addLink($concatenation, $contains, 'CONCAT');
        $fullcode[]     = $contains->fullcode;
        $concat[]       = $contains;
        $noDelimiter   .= $contains->noDelimiter;
        $contains->rank = ++$rank;

        $concatenation->fullcode    = implode(' . ', $fullcode);
        $concatenation->noDelimiter = $noDelimiter;
        $concatenation->count       = $rank + 1;

        $this->pushExpression($concatenation);
        $this->runPlugins($concatenation, $concat);
        $this->calls->addNoDelimiterCall($concatenation);

        $this->checkExpression();

        return $concatenation;
    }

    private function processInstanceof(): AtomInterface {
        $current = $this->id;
        $instanceof = $this->addAtom('Instanceof', $current);

        $left = $this->popExpression();
        $this->addLink($instanceof, $left, 'VARIABLE');

        $finals = $this->precedence->get($this->tokens[$this->id][0]);
        do {
            $right = $this->processNext();
        } while (!$this->nextIs($finals));
        $this->popExpression();

        $this->addLink($instanceof, $right, 'CLASS');

        $this->getFullnspath($right, 'class', $right);
        $this->calls->addCall(Calls::A_CLASS, $right->fullnspath, $right);
        $this->getFullnspath($right, 'class', $right);

        $instanceof->fullcode = $left->fullcode . ' ' . $this->tokens[$current][1] . ' ' . $right->fullcode;
        $instanceof->ws->opening  = $this->tokens[$current][1] . $this->tokens[$current][4];

        $this->runPlugins($instanceof, array('VARIABLE' => $left,
                                             'CLASS'    => $right));
        $this->pushExpression($instanceof);

        return $instanceof;
    }

    private function processKeyvalue(): AtomInterface {
        return $this->processOperator('Keyvalue', $this->precedence->get($this->tokens[$this->id][0]), array('INDEX', 'VALUE'));
    }

    private function processPhpdoc(): AtomInterface {
        $id = count($this->phpDocs);
        $this->phpDocs[$id] = $this->tokens[$this->id];
        $this->phpDocs[$id]['id'] = $this->id;

        return $this->atomVoid;
    }

    private function makePhpdoc(AtomInterface $node): void {
        foreach($this->phpDocs as $phpDoc) {
            $atom = $this->addAtom('Phpdoc', $phpDoc['id']);
            $atom->code = $phpDoc[1];
            $atom->fullcode = $phpDoc[1];
            $atom->ws->closing = $phpDoc[4];
            $this->addLink($node, $atom, 'PHPDOC');
        }

        $this->phpDocs = array();
    }

    private function processAttribute(): AtomInterface {
        do {
            // This is for the final comma , ]
            if ($this->nextIs(array($this->phptokens::T_CLOSE_BRACKET))) {
                $this->moveToNext();
                break;
            }

            $current = $this->id;
            // Attributes are only classes, not functions
            $this->contexts->toggleContext(Context::CONTEXT_NEW);
            $attribute = $this->processNext();

            $this->contexts->toggleContext(Context::CONTEXT_NEW);
            $attribute->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];

            // Todo : currently handled as a 'new'
            $this->getFullnspath($attribute, 'class', $attribute);
            // This may be a methodcall, with a wrongly build fullnspath.
            if (($id = strpos($attribute->fullnspath, '(')) !== false) {
                $attribute->fullnspath = substr($attribute->fullnspath, 0, $id);
            }
            $this->calls->addCall(Calls::A_CLASS, $attribute->fullnspath, $attribute);

            $this->popExpression();
            $attribute->ws->closing .= $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4];

            $this->attributes[] = $attribute;
            $this->moveToNext(); // skip ]
        } while ($this->nextIs(array($this->phptokens::T_COMMA), 0));

        return $attribute;
    }

    private function processBitshift(): AtomInterface {
        // Classic bitshift expression
        return $this->processOperator('Bitshift', $this->precedence->get($this->tokens[$this->id][0]));
    }

    private function processIsset(): AtomInterface {
        $current = $this->id;

        $atom = ucfirst(mb_strtolower($this->tokens[$current][1]));
        $this->moveToNext();
        $argumentsList = array();
        $functioncall = $this->processArguments($atom, array(), $argumentsList);
        $this->makePhpdoc($functioncall);
        $functioncall->position = $this->tokens[$current][3];

        $argumentsFullcode = $functioncall->fullcode;

        $functioncall->code       = $this->tokens[$current][1];
        $functioncall->fullcode   = $this->tokens[$current][1] . '(' . $argumentsFullcode . ')';
        $functioncall->token      = $this->getToken($this->tokens[$current][0]);
        $functioncall->fullnspath = '\\' . mb_strtolower($this->tokens[$current][1]);
        $functioncall->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4] . $this->tokens[$current + 1][1] . $this->tokens[$current + 1][4];
        $functioncall->ws->closing = $this->tokens[$this->id][1] . $this->tokens[$this->id][4];

        $this->runPlugins($functioncall, $argumentsList);

        $this->pushExpression($functioncall);

        $this->checkExpression();

        return $functioncall;
    }

    private function processEcho(): AtomInterface {
        $current = $this->id;

        $argumentsList = array();
        $functioncall = $this->processArguments('Echo',
                                                array($this->phptokens::T_SEMICOLON,
                                                      $this->phptokens::T_CLOSE_TAG,
                                                      $this->phptokens::T_END,
                                                     ),
                                                $argumentsList);
        $argumentsFullcode = $functioncall->fullcode;

        $functioncall->code        = $this->tokens[$current][1];
        $functioncall->fullcode    = $this->tokens[$current][1] . ' ' . $argumentsFullcode;
        $functioncall->token       = $this->getToken($this->tokens[$current][0]);
        $functioncall->fullnspath  = '\\' . mb_strtolower($this->tokens[$current][1]);
        $functioncall->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $functioncall->ws->closing = '';

        $this->pushExpression($functioncall);

        $this->runPlugins($functioncall, $argumentsList);

        // processArguments goes too far, up to ;
        --$this->id;

        if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        }

        return $functioncall;
    }

    private function processHalt(): AtomInterface {
        $halt = $this->addAtom('Halt', $this->id);
        $halt->fullcode = $this->tokens[$this->id][1];
        $halt->ws->opening = $this->tokens[$this->id][1] . $this->tokens[$this->id][4] .
                             $this->tokens[$this->id + 1][1] . $this->tokens[$this->id + 1][4] .  // (
                             $this->tokens[$this->id + 2][1] . $this->tokens[$this->id + 2][4] .  // )
                             $this->tokens[$this->id + 3][1] . $this->tokens[$this->id + 3][4] ;  // ;


        $this->moveToNext(); // skip halt
        $this->moveToNext(); // skip (
        // Skipping all arguments. This is not a function!
        $this->moveToNext(); // skip ;

        $this->sequence->ws->separators[] = '';
        $this->addToSequence($halt);

        return $halt;
    }

    private function processPrint(): AtomInterface {
        $current = $this->id;

        if ($this->nextIs(array($this->phptokens::T_INCLUDE,
                                $this->phptokens::T_INCLUDE_ONCE,
                                $this->phptokens::T_REQUIRE,
                                $this->phptokens::T_REQUIRE_ONCE,
                                ), 0)) {
            $functioncall = $this->addAtom('Include', $current);
        } else {
            $functioncall = $this->addAtom('Print', $current);
        }
        $this->makePhpdoc($functioncall);

        $noSequence = $this->contexts->isContext(Context::CONTEXT_NOSEQUENCE);
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }

        $finals = $this->precedence->get($this->tokens[$this->id][0]);
        while (!$this->nextIs($finals)) {
            $this->processNext();
        }
        if ($noSequence === false) {
            $this->contexts->toggleContext(Context::CONTEXT_NOSEQUENCE);
        }

        $index = $this->popExpression();
        $index->rank = 0;
        $this->addLink($functioncall, $index, 'ARGUMENT');

        $functioncall->fullcode   = $this->tokens[$current][1] . ' ' . $index->fullcode;
        $functioncall->count      = 1; // Only one argument for print
        $functioncall->fullnspath = '\\' . mb_strtolower($this->tokens[$current][1]);
        $functioncall->ws->opening = $this->tokens[$current][1] . $this->tokens[$current][4];
        $functioncall->ws->closing = '';

        $this->pushExpression($functioncall);
        $this->runPlugins($functioncall, array('ARGUMENT' => $index));

        $this->checkExpression();

        return $functioncall;
    }

    //////////////////////////////////////////////////////
    /// generic methods
    //////////////////////////////////////////////////////
    private function addAtom(string $atomName, int $id = null): AtomInterface {
        if (!in_array($atomName, GraphElements::$ATOMS, \STRICT_COMPARISON)) {
            throw new LoadError('Undefined atom ' . $atomName . ':' . $this->filename . ':' . __LINE__);
        }

        $line = $this->tokens[$this->id][2] ?? $this->tokens[$this->id - 1][2] ?? $this->tokens[$this->id - 2][2] ?? -1;
        $atom = $this->atomGroup->factory($atomName, $line, $this->tokens[$this->id][4] ?? '');
        $atom->position = $this->tokens[$id][3] ?? $this->tokens[$this->id][3] ?? 0;

        if ($id !== null) {
            $atom->code  = $this->tokens[$id][1];
            $atom->token = $this->getToken($this->tokens[$id][0]);
        }

        $this->atoms[$atom->id] = $atom;
        if ($atom->id < $this->minId) {
            $this->minId = $atom->id;
        }

        return $atom;
    }

    private function addAtomVoid(): AtomInterface {
        $void = $this->addAtom('Void');
        $void->code        = 'Void';
        $void->fullcode    = self::FULLCODE_VOID;
        $void->token       = $this->phptokens::T_VOID;
        $this->makePhpdoc($void);

        $this->runPlugins($void);

        return $void;
    }

    private function addLink(AtomInterface $origin, AtomInterface $destination, string $label): void {
        if (!in_array($label, array_merge(GraphElements::$LINKS, GraphElements::$LINKS_EXAKAT), \STRICT_COMPARISON)) {
            debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
            throw new LoadError('Undefined link ' . $label . ' for atom ' . $origin->atom . ' : ' . $this->filename . ':' . $origin->line);
        }

        if ($origin->id < $this->minId) {
            $this->relicat[] = array($origin->id, $destination->id);
        } elseif ($destination->id < $this->minId) {
            $this->relicat[] = array($origin->id, $destination->id);
        } else {
            $this->links[] = array($label, $origin->id, $destination->id);
        }
    }

    private function pushExpression(AtomInterface $atom): void {
        $this->expressions[] = $atom;
    }

    private function hasExpression(): bool {
        return !empty($this->expressions);
    }

    private function popExpression(): AtomInterface {
        if (empty($this->expressions)) {
            $id = $this->addAtomVoid();
        } else {
            $id = array_pop($this->expressions);
        }

        return $id;
    }

    private function checkTokens(string $filename): void {
        if (!empty($this->expressions)) {
            throw new LoadError( "Warning : expression is not empty in $filename : " . count($this->expressions));
        }

        if (($count = $this->contexts->getCount(Context::CONTEXT_NOSEQUENCE)) !== false) {
            throw new LoadError( "Warning : context for sequence is not back to 0 in $filename : it is $count\n");
        }

        if (($count = $this->contexts->getCount(Context::CONTEXT_NEW)) !== false) {
            throw new LoadError( "Warning : context for new is not back to 0 in $filename : it is $count\n");
        }

        if (($count = $this->contexts->getCount(Context::CONTEXT_FUNCTION)) !== false) {
            throw new LoadError( "Warning : context for function is not back to 0 in $filename : it is $count\n");
        }

        if (($count = $this->contexts->getCount(Context::CONTEXT_CLASS)) !== false) {
            throw new LoadError( "Warning : context for class is not back to 0 in $filename : it is $count\n");
        }
    }

    private function processDefineAsClassalias(array $argumentsId): void {
        if (!isset($argumentsId[0]) ||
            $argumentsId[0]->atom === 'Void') {

            return;
        }

        if (!isset($argumentsId[0]) ||
            !isset($argumentsId[1])) {

            // no/partial argument used with class_alias. Just bail.
            return;
        }

        if (empty($argumentsId[0]->noDelimiter) ||
            empty($argumentsId[1]->noDelimiter)   ) {
            $argumentsId[0]->fullnspath = '\\'; // cancels it all
            $argumentsId[1]->fullnspath = '\\';

            return;
        }

        if (preg_match('/[$ #?;%^\*\'\"\. <>~&,|\(\){}\[\]\/\s=+!`@\-]/is', $argumentsId[0]->noDelimiter)) {
            $argumentsId[0]->fullnspath = '\\'; // cancels it all
            $argumentsId[1]->fullnspath = '\\';

            return; // Can't be a class anyway.
        }

        if (preg_match('/[$ #?;%^\*\'\"\. <>~&,|\(\){}\[\]\/\s=+!`@\-]/is', $argumentsId[1]->noDelimiter)) {
            $argumentsId[0]->fullnspath = '\\'; // cancels it all
            $argumentsId[1]->fullnspath = '\\';

            return; // Can't be a class anyway.
        }

        $fullnspathClass = makeFullNsPath($argumentsId[0]->noDelimiter, \FNP_NOT_CONSTANT);
        $argumentsId[0]->fullnspath = $fullnspathClass;

        $fullnspathAlias = makeFullNsPath($argumentsId[1]->noDelimiter, \FNP_NOT_CONSTANT);
        $argumentsId[1]->fullnspath = $fullnspathAlias;

        $this->calls->addCall(Calls::A_CLASS, $fullnspathClass, $argumentsId[0]);
        $this->calls->addDefinition(Calls::A_CLASS, $fullnspathAlias, $argumentsId[1]);
    }

    private function processDefineAsConstants(AtomInterface $const, AtomInterface $name, bool $caseInsensitive = self::CASE_INSENSITIVE): void {
        if (empty($name->noDelimiter)) {
            $name->fullnspath = '\\';
            return;
        }

        if (preg_match('/[$ #?;%^\*\'\"\. <>~&,|\(\){}\[\]\/\s=+!`@\-]/is', $name->noDelimiter)) {
            return; // Can't be a constant anyway.
        }

        $fullnspath = makeFullNsPath($name->noDelimiter, \FNP_CONSTANT);
        if ($name->noDelimiter[0] === '\\') {
            // Added a second \\ when the string already has one. Actual PHP behavior
            $fullnspath = "\\$fullnspath";
        }

        $this->calls->addDefinition(Calls::CONST, $fullnspath, $const);
        $name->fullnspath = $fullnspath;

        if ($caseInsensitive === true) {
            $this->calls->addDefinition(Calls::CONST, mb_strtolower($fullnspath), $const);
        }
    }

    private function saveFiles(): void {
        $this->loader->saveFiles($this->config->tmp_dir, $this->atoms, $this->links);
        $this->reset();
    }

    private function startSequence(): void {
        $this->sequence = $this->addAtom('Sequence');
        $this->sequence->code        = ';';
        $this->sequence->fullcode    = ' ' . self::FULLCODE_SEQUENCE . ' ';
        $this->sequence->token       = 'T_SEMICOLON';
        $this->sequence->bracket     = self::NOT_BRACKET;
        $this->sequence->ws->closing = '';

        $this->sequences->start($this->sequence);
    }

    private function addToSequence(AtomInterface $element): void {
        $this->addLink($this->sequence, $element, 'EXPRESSION');

        $this->sequences->add($element);
    }

    private function endSequence(): void {
        $elements = $this->sequences->getElements();
        $this->runPlugins($this->sequence, $elements);

        $this->sequence = $this->sequences->end();
    }

    // token may be string or int
    private function getToken($token): string {
        return $this->php->getTokenName($token);
    }

    private function getFullnspath(AtomInterface $name, string $type = 'class', AtomInterface $apply = null): void {
        assert($apply !== null, "\$apply can't be null in " . __METHOD__);

        // Handle static, self, parent and PHP natives function
        if (isset($name->absolute) && ($name->absolute === self::ABSOLUTE)) {
            if ($type === 'const') {
                if (($use = $this->uses->get('class', mb_strtolower($name->fullnspath))) instanceof AtomInterface) {
                    $apply->fullnspath = mb_strtolower($name->fullnspath);
                    return;
                }
                $fullnspath = preg_replace_callback('/^(.*)\\\\([^\\\\]+)$/', function (array $r): string {
                    return mb_strtolower($r[1]) . '\\' . $r[2];
                }, $name->fullcode);
                $apply->fullnspath = $fullnspath;
                return;
            }
            $apply->fullnspath = mb_strtolower($name->fullcode);

            return;
        }

        if (!$name->isA(array('Nsname', 'Identifier', 'Name', 'String', 'Null', 'Boolean', 'Static', 'Parent', 'Self', 'Newcall', 'Newcallname', 'This'))) {
            // No fullnamespace for non literal namespaces
            $apply->fullnspath = '';
            return;
        } elseif (in_array($name->token, array('T_ARRAY', 'T_EVAL', 'T_ISSET', 'T_EXIT', 'T_UNSET', 'T_ECHO', 'T_PRINT', 'T_LIST', 'T_EMPTY', ), \STRICT_COMPARISON)) {
            // For language structures, it is always in global space, like eval or list
            $apply->fullnspath = '\\' . mb_strtolower($name->code);
            return;
        } elseif (mb_strtolower(substr($name->fullcode, 0, 10)) === 'namespace\\') {

            $details = explode('\\', $name->fullcode);
            if ($type === 'const') {
                array_shift($details); // namespace
                $const = array_pop($details);
                $fullnspath = mb_strtolower(implode('\\', $details)) . '\\' . $const;
            } else {
                array_shift($details); // namespace
                $fullnspath = '\\' . mb_strtolower(implode('\\', $details));
            }

            $apply->fullnspath = substr($this->namespace, 0, -1) . $fullnspath;
            return;
        } elseif ($name->isA(array('Static', 'Self', 'This'))) {
            if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                $apply->fullnspath = self::FULLNSPATH_UNDEFINED;
                return;
            } else {
                $apply->fullnspath = $this->currentClassTrait->getCurrent()->fullnspath;
                    return;
            }
        } elseif ($name->atom === 'Newcall' && mb_strtolower($name->code) === 'static') {
            if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                $apply->fullnspath = self::FULLNSPATH_UNDEFINED;
                return;
            } else {
                $apply->fullnspath = $this->currentClassTrait->getCurrent()->fullnspath;
                    return;
            }
        } elseif ($name->atom === 'Parent') {
            $apply->fullnspath = '\\parent';
            return;
        } elseif ($name->isA(array('Boolean', 'Null'))) {
            $apply->fullnspath = '\\' . mb_strtolower($name->fullcode);
                    return;
        } elseif ($name->isA(array('Identifier', 'Name', 'Newcall', 'Newcallname'))) {
            if ($name->isA(array('Newcall', 'Name', 'Newcallname'))) {
               $fnp = mb_strtolower($name->code);
            } else {
               $fnp = $name->code;
            }

            if (($offset = strpos($fnp, '\\')) === false) {
                $prefix = $fnp;
            } else {
                $prefix = substr($fnp, 0, $offset);
            }

            // This is an identifier, self or parent
            if ($type === 'class' && ($use = $this->uses->get('class',mb_strtolower($fnp) )) instanceof AtomInterface) {
                $this->addLink($name, $use, 'USED');
                $apply->fullnspath = $use->fullnspath;
                return;

            } elseif ($type === 'class' && ($use = $this->uses->get('class', $prefix)) instanceof AtomInterface) {
                $this->addLink($name, $use, 'USED');
                $apply->fullnspath = $use->fullnspath . '\\' . preg_replace('/^' . $prefix . '\\\\/', '', $fnp);
                    return;

            } elseif ($type === 'const') {
                if (($use = $this->uses->get('const', $name->code)) instanceof AtomInterface) {
                    $this->addLink($use, $name, 'USED');
                    $apply->fullnspath = $use->fullnspath;
                    return;
                }

                if (($use = $this->uses->get('class', mb_strtolower($name->fullnspath))) instanceof AtomInterface) {
                    $apply->fullnspath = mb_strtolower($name->fullnspath);
                    return;
                }

                $apply->fullnspath = $this->namespace . $name->noDelimiter;
                return;

            } elseif ($type === 'function' && ($use = $this->uses->get('function', $prefix)) instanceof AtomInterface) {
                $this->addLink($use, $name, 'USED');
                $apply->fullnspath = $use->fullnspath;
                return;

            } else {
                $apply->fullnspath = $this->namespace . mb_strtolower($name->fullcode);
                return;
            }

        } elseif ($name->atom === 'String' && isset($name->noDelimiter)) {
            if (in_array(mb_strtolower($name->noDelimiter), array('self', 'static'), \STRICT_COMPARISON)) {
                if ($this->currentClassTrait->getCurrent() === ClassTraitContext::NO_CLASS_TRAIT_CONTEXT) {
                    $apply->fullnspath = self::FULLNSPATH_UNDEFINED;
                } else {
                    $apply->fullnspath = $this->currentClassTrait->getCurrent()->fullnspath;
                }
                return;
            }

            $prefix =  str_replace('\\\\', '\\', mb_strtolower($name->noDelimiter));
            $prefix = "\\$prefix";

            // define doesn't care about use...
            $apply->fullnspath = $prefix;
            return;
        } else {
            // Finally, the case for a nsname
            $prefix = mb_strtolower( substr($name->code, 0, strpos($name->code . '\\', '\\')) );

            if (($use = $this->uses->get($type, $prefix)) instanceof AtomInterface) {
                $this->addLink( $name, $use, 'USED');
                $apply->fullnspath = $use->fullnspath . mb_strtolower( substr($name->fullcode, strlen($prefix)) ) ;
                    return;
            } elseif ($type === 'const') {
                $parts = explode('\\', $name->fullcode);
                $last = array_pop($parts);
                $fullnspath = $this->namespace . mb_strtolower(implode('\\', $parts)) . '\\' . $last;
                $apply->fullnspath = $fullnspath;
                    return;
            } else {
                $apply->fullnspath = $this->namespace . mb_strtolower($name->fullcode);
                    return;
            }
        }
    }

    private function setNamespace(string $namespace = self::NO_NAMESPACE): void {
        if ($namespace === self::NO_NAMESPACE) {
            $this->namespace = '\\';
            $this->uses = new Fullnspaths();
        } else {
            $this->namespace = mb_strtolower($namespace) . '\\';
            if ($this->namespace[0] !== '\\') {
                $this->namespace = '\\' . $this->namespace;
            }
        }
    }

    private function addNamespaceUse(AtomInterface $origin, AtomInterface $alias, string $useType, AtomInterface $use): string {
        if ($origin !== $alias) { // Case of A as B
            // Alias is the 'As' expression.
            $offset = strrpos($alias->fullcode, ' as ');
            if ($useType === 'const') {
                $return = substr($alias->fullcode, $offset + 4);
            } else {
                $return = mb_strtolower(substr($alias->fullcode, $offset + 4));
            }
        } elseif (($offset = strrpos($alias->code, '\\')) !== false) {
            // namespace with \
            $return = substr($alias->code, $offset + 1);
        } else {
            // namespace without \
            $return = $alias->code;
        }

        if ($useType !== 'const') {
            $return = mb_strtolower($return);
        }

        $this->uses->set($useType, $return, $use);

        return $return;
    }

    private function logTime(string $step): void {
        $this->log->log($step);
    }

    private function finishWithAlternative(bool $isColon): void {
        if ($isColon === self::ALTERNATIVE_SYNTAX) {
            $this->moveToNext(); // Skip endforeach
            if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), 0)) {
                --$this->id;
            }
            $this->processSemicolon();
            if ($this->nextIs(array($this->phptokens::T_SEMICOLON))) {
                $this->moveToNext();
            }
        } else {
            if ($this->nextIs(array($this->phptokens::T_CLOSE_TAG), 0)) {
                --$this->id;
            }

            $atom = $this->popExpression();
            $this->sequence->ws->separators[] = '';
            $this->addToSequence($atom);
        }
    }

    private function checkExpression(): void {
        $this->checkPhpdoc();

        if ( !$this->contexts->isContext(Context::CONTEXT_NOSEQUENCE) && $this->nextIs(array($this->phptokens::T_CLOSE_TAG))) {
            $this->processSemicolon();
        }
    }

    private function whichSyntax(int $current, int $colon): bool {
        return in_array($this->tokens[$current][0], array($this->phptokens::T_FOR,
                                                          $this->phptokens::T_FOREACH,
                                                          $this->phptokens::T_WHILE,
                                                          $this->phptokens::T_DO,
                                                          $this->phptokens::T_DECLARE,
                                                          $this->phptokens::T_SWITCH,
                                                          $this->phptokens::T_IF,
                                                          $this->phptokens::T_ELSEIF,
                                                         ), \STRICT_COMPARISON) &&
               ($this->tokens[$colon][0] === $this->phptokens::T_COLON) ?
                self::ALTERNATIVE_SYNTAX :
                self::NORMAL_SYNTAX;
    }

    private function makeGlobal(AtomInterface $element): void {
        if ($element->atom === 'Globaldefinition') {
            $name = $element->code;
        } elseif ($element->atom === 'Variabledefinition') {
            $name = $element->code;
        } elseif ($element->atom === 'Phpvariable') {
            $name = $element->code;
        } elseif (!empty($element->noDelimiter)) {
            $name = '$' . $element->noDelimiter;
        } else {
            return;
        }

        if (!isset($this->theGlobals[$name])) {
            $this->theGlobals[$name] = $this->addAtom('Virtualglobal');
            $this->theGlobals[$name]->fullcode = "[global {$element->code}]";
            $this->theGlobals[$name]->code = $element->code;
            $this->theGlobals[$name]->lccode = $element->code;
            $this->theGlobals[$name]->line = -1;
            $this->theGlobals[$name]->globalvar = ltrim($name, '$');
        }
    }

    private function nextIs(array $tokens, int $offset = 1): bool {
        return in_array($this->tokens[$this->id + $offset][0], $tokens, \STRICT_COMPARISON);
    }

    private function moveToNext(int $token = 0): int {
        ++$this->id; // Skip default
        if (!empty($token)) {
            assert($this->tokens[$this->id][0] === $token, 'Not the expected token when moving : found ' . $this->tokens[$this->id][1]);
        }

        return $this->id;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Reports\Reports;
use Symfony\Component\Yaml\Yaml as Symfony_Yaml;
use Exakat\Exceptions\NoSuchReport;

class Catalog extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        $data = array();

        // List of analysis
        $rulesets = $this->rulesets->listAllRulesets();
        sort($rulesets);
        $rulesets = array_map( function ($x) {
            if (strpos($x, ' ') !== false) {
                $x = '"' . $x . '"';
            }
            return $x;
        }, $rulesets);
        $data['rulesets'] = $rulesets;

        // List of reports
        $r = Reports::$FORMATS;
        $reports = array();
        foreach($r as $report) {
            try {
                Reports::getInstance($report);
                $reports[] = $report;
            } catch (NoSuchReport $e) {
                // just ignore this
            }
        }
        sort($reports);
        $data['reports'] = $reports;

        // List of rules
        $rules = exakat('rulesets')->listAllAnalyzer();
        sort($rules);
        $data['rules'] = $rules;

        if ($this->config->json === true) {
            print json_encode($data);
        } elseif ($this->config->yaml === true) {
            print Symfony_Yaml::dump($data);
        } else {
            $display = '';

            foreach($data as $section => $list) {
                $display .= count($list) . " $section : \n";
                $display .= '   ' . implode("\n   ", $list) . "\n";
            }

            print $display;
        }
    }
}

?>
   Bud1                                                                 	       e r sbwspbl                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              	    H e l p e r sbwspblob   bplist00		]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar			_{{2076, 13}, {1107, 767}}	%1=I`myz{|}~                                H e l p e r sdsclbool    H e l p e r slsvCblob  bplist00	
GHGIJ_viewOptionsVersion_showIconPreview_calculateAllSizesWcolumns_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates 	"&+05:?CWvisibleUwidthYascendingZidentifier		Tname#Xubiquity!	\dateModified%[dateCreated(*	aTsize-/	s	Tkind24d	Ulabel79K	Wversion<>,	XcommentsB^dateLastOpenedFYdateAdded#        #@(      Tname#@0      	   2 D X ` r {                 *345AJKMNS\]_`enoqrx             L                  H e l p e r slsvpblob  bplist00	
FGFHI_viewOptionsVersion_showIconPreview_calculateAllSizesWcolumns_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates 	!&*.38=BXcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameUindexUwidthYascendingWvisible,	"#'#	+#/0a	45d	9:s		>?K	C		#        #@(      Tname#@0      	   2 D X ` r {            "(.8@BEFGPRTUV_abclnopy{}~             K                  H e l p e r svSrnlong      	 L o a d F i n a lbwspblob   bplist00		]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar			_{{1624, -95}, {1052, 875}}	%1=I`myz{|}~                               	 L o a d F i n a llsvCblob  bplist00	
GHGIJ_viewOptionsVersion_showIconPreview_calculateAllSizesWcolumns_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates 	"&+05:?CWvisibleUwidthYascendingZidentifier		Tname#Xubiquity!	\dateModified%[dateCreated(*	aTsize-/	s	Tkind24d	Ulabel79K	Wversion<>,	XcommentsB^dateLastOpenedFYdateAdded#        #@(      Tname#@0      	   2 D X ` r {                 *345AJKMNS\]_`enoqrx             L                 	 L o a d F i n a llsvpblob  bplist00	
FGFHI_viewOptionsVersion_showIconPreview_calculateAllSizesWcolumns_scrollPositionYXtextSize_scrollPositionXZsortColumnXiconSize_useRelativeDates 	!&*.38=BXcomments^dateLastOpened\dateModified[dateCreatedTsizeUlabelTkindWversionTnameUindexUwidthYascendingWvisible,	"#'#	+#/0a	45d	9:s		>?K	C		#        #@(      Tname#@0      	   2 D X ` r {            "(.8@BEFGPRTUV_abclnopy{}~             K                 	 L o a d F i n a lvSrnlong                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              E                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         DSDB                                 `                                    (      0          @                                                @                                                @       ndWversionTnameUindexUwidthYascendingWvisible,	"#'#	+#/0a	45d	9:s		>?K	C		#        #@(      Tname#@0      	   2 D X ` r {            "(.8@BEFGPRTUV_abclnopy{}~             K                  H e l p e r svSrnlong      	 L o a d F i n a lbwspblob   bplist00		]ShowStatusBar[ShowPathbar[ShowToolbar[ShowTabView_ContainerShowSidebar\WindowBounds[ShowSidebar			_{{1624, -95}, {1052, 875}}	%1=I`myz{|}~                               	 L o a d F i n a llsvCblob  bplist00	
GHGIJ_viewOptionsVersion_showIconPreview_calculat<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

class Install extends Tasks {
    public const CONCURENCE = self::NONE;

    public const TINKERGRAPH_VERSION = '3.4.13';

    public function run(): void {
        $error = array();

        $res = shell_exec('java -version 2>&1') ?? '';
        if (preg_match('/(java version|openjdk )/', $res) === 0) {
            $error[] = 'Please install Java or openJdk, 1.8 or more recent';
        } else {
            print "Java : OK\n";
        }

        $res = shell_exec('zip -help 2>&1') ?? '';
        if (strpos($res, 'Zip 3.0') === false) {
            $error[] = 'Please install Zip 3.0 or more recent';
        } else {
            print "Zip : OK\n";
        }

        if (!empty($error)) {
            $error[] = 'Fix the above ' . count($error) . ' error' . (count($error) > 1 ? 's' : '') . " and try again\n";
            print implode(PHP_EOL, $error) . PHP_EOL;
            die();
        }

        if (file_exists($this->config->projects_root . '/tinkergraph') && is_dir($this->config->projects_root . '/tinkergraph')) {
            print "Tinkergraph is already installed. Omitting\n";
        } else {
            print "Starting download of tinkergraph.\n";
            $tinkerpop = file_get_contents('https://www.exakat.io/versions/apache-tinkerpop-gremlin-server-' . self::TINKERGRAPH_VERSION . '-bin.zip') ?? '';

            if (hash('sha256', $tinkerpop) !== substr(@file_get_contents('https://www.exakat.io/versions/apache-tinkerpop-gremlin-server-' . self::TINKERGRAPH_VERSION . '-bin.zip.sha256') ?? '', 0, 64)) {
                die('SHA256 checksum doesn\'t match the downloaded version of tinkerpop. Aborting install' . PHP_EOL);
            } else {
                print "Gremlin server checksum OK\n";
            }
            file_put_contents($this->config->projects_root . '/apache-tinkerpop-gremlin-server-' . self::TINKERGRAPH_VERSION . '-bin.zip', $tinkerpop);

            // Install tinkergraph
            shell_exec('cd ' . $this->config->projects_root . '; unzip apache-tinkerpop-gremlin-server-' . self::TINKERGRAPH_VERSION . '-bin.zip; mv apache-tinkerpop-gremlin-server-' . self::TINKERGRAPH_VERSION . ' tinkergraph; rm -rf apache-tinkerpop-gremlin-server-' . self::TINKERGRAPH_VERSION . '-bin.zip');
            print "Tinkergraph installed\n";
        }

        if (file_exists($this->config->projects_root . '/tinkergraph/ext/neo4j-gremlin')) {
            print "Neo4j for gremlin is already installed. Omitting\n";
        } else {
            print "Starting download of tinkergraph plugin Neo4j.\n";
            // Install neo4j
            shell_exec('cd ' . $this->config->projects_root . '/tinkergraph; ./bin/gremlin-server.sh install org.apache.tinkerpop neo4j-gremlin ' . self::TINKERGRAPH_VERSION);
            print "Neo4j for Tinkergraph installed\n";
        }

        shell_exec(PHP_BINARY . ' ' . $this->config->executable . ' doctor');

        $ini = file_get_contents($this->config->projects_root . '/config/exakat.ini');
        if (preg_match('/graphdb\s*=\s*\'nogremlin\';/', $ini)) {
            // check for nogremlin configuration
            if (file_exists($this->config->projects_root . '/tinkergraph/ext/neo4j-gremlin')) {
                $ini = preg_replace('/graphdb\s*=\s*\'nogremlin\';/', 'graphdb\s*=\s*\'gsneo4jv3\';', $ini);
                $ini = str_replace(';gsneo4jv3_', 'gsneo4jv3_', $ini);

                file_put_contents($this->config->projects_root . '/config/exakat.ini', $ini);
            }
        } else {
            $ini = preg_replace('/graphdb\s*=\s*\'nogremlin\';/', 'graphdb\s*=\s*\'tinkergraphv3\';', $ini);
            $ini = str_replace(';tinkergraphv3_', 'tinkergraphv3_', $ini);

            file_put_contents($this->config->projects_root . '/config/exakat.ini', $ini);
        }

        print shell_exec(PHP_BINARY . ' ' . $this->config->executable . ' doctor');
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy - Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Analyzer\Analyzer;
use Exakat\Analyzer\Dump\AnalyzerDump;
use Exakat\Config;
use Exakat\Exceptions\MissingGremlin;
use Exakat\Exceptions\NoSuchAnalyzer;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\NoSuchRuleset;
use Exakat\GraphElements;
use Exakat\Log;
use Exakat\Query\Query;
use Exakat\Dump\Dump as DumpDb;
use Exakat\Helpers\Timer;

class Dump extends Tasks {
    public const CONCURENCE = self::DUMP;

    private $files = array();

    protected $logname = self::LOG_NONE;

    private $linksDown = '';
    private $dump      = null;

    public const WAITING_LOOP = 1000;

    private int $argumentsId     = 0;
    private int $methodCount     = 0;
    private int $propertyCount   = 0;
    private int $constantCount   = 0;
    private int $classConstCount = 0;
    private int $citCount        = 0;

    private array $methodIds     = array();
    private array $classConstIds = array();
    private array $propertyIds   = array();

    public function __construct(bool $subTask = self::IS_NOT_SUBTASK) {
        parent::__construct($subTask);

        $this->log = new Log('dump',
                             $this->config->project_dir);

        $this->linksDown = GraphElements::linksAsList();
    }

    public function run(): void {
        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($this->config->project);
        }

        if ($this->config->gremlin === 'NoGremlin') {
            throw new MissingGremlin();
        }

        $projectInGraph = $this->gremlin->query('g.V().hasLabel("Project").values("code")');
        if (empty($projectInGraph)) {
            throw new NoSuchProject($this->config->project);
        }
        if (!isset($projectInGraph[0])) {
            throw new NoSuchProject($this->config->project);
        }

        $projectInGraph = $projectInGraph[0];

// TODO
//        $this->sqliteFilePrevious = $this->config->dump_previous;
// also baseline

        // move this to .dump.sqlite then rename at the end, or any imtermediate time
        // Mention that some are not yet arrived in the snitch
        $this->addSnitch();

        if ($this->config->update !== true && file_exists($this->config->dump)) {
            unlink($this->config->dump);
        }
        $this->dump = DumpDb::factory($this->config->dump, DumpDb::INIT);

        if ($this->config->collect === true) {
            display('Collecting data');

            $this->collect();
        }

        $this->loadSqlDump();

        $counts = array();
        $datastore = new \Sqlite3($this->config->datastore, \SQLITE3_OPEN_READONLY);
        $datastore->busyTimeout(\SQLITE3_BUSY_TIMEOUT);
        $res = $datastore->query('SELECT * FROM analyzed');
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $counts[$row['analyzer']] = (int) $row['counts'];
        }
        $this->log->log('count analyzed : ' . count($counts) . "\n");
        $this->log->log('counts ' . implode(', ', $counts) . "\n");
        $datastore->close();
        unset($datastore);

        if (!empty($this->config->project_rulesets)) {
            $ruleset = $this->config->project_rulesets;
            if ($ruleset === array('None')) {
                $rulesets = array();
            } else {
                $rulesets = $this->rulesets->getRulesetsAnalyzers($ruleset);
                if (empty($rulesets)) {
                    $r = $this->rulesets->getSuggestionRuleset($ruleset);
                    if (!empty($r)) {
                        echo 'did you mean : ', implode(', ', str_replace('_', '/', $r)), "\n";
                    }

                    throw new NoSuchRuleset(implode(', ', $ruleset));
                }
                $missing = $this->processResultsRuleset($ruleset, $counts);
                $this->expandRulesets();
                $this->collectHashAnalyzer();

                if ($missing === 0) {
                    $this->storeToDumpArray('themas', array_map(function (string $x) { return array('', $x); }, $ruleset));
                    $rulesets = array();
                }
            }

        } elseif (!empty($this->config->program)) {
            $analyzer = $this->config->program;
            if(is_array($analyzer)) {
                $rulesets = $analyzer;
            } else {
                $rulesets = array($analyzer);
            }

            $rulesets = array_unique($rulesets);
            foreach($rulesets as $id => $ruleset) {
                if (!$this->rulesets->getClass($ruleset)) {
                    display('No such analyzer as ' . $ruleset . '. Omitting.');
                    unset($rulesets[$id]);
                }
            }

            if (empty($rulesets)) {
                throw new NoSuchAnalyzer($rulesets, $this->rulesets);
            }

            display('Processing ' . count($rulesets) . ' analyzer' . (count($rulesets) > 1 ? 's' : '') . ' : ' . implode(', ', $rulesets));

            if(count($rulesets) > 1) {
                $this->processResultsList($rulesets, $counts);
                $this->expandRulesets();
                $this->collectHashAnalyzer();
            } else {
                $analyzer = array_pop($rulesets);
                if (isset($counts[$analyzer])) {
                    $this->processMultipleResults(array($analyzer), $counts);
                    $this->collectHashAnalyzer();
                    $rulesets = array();
                } else {
                    display("$analyzer is not run yet.");
                }
            }

        } else {
            $rulesets = array();
        }

        $this->log->log('Still ' . count($rulesets) . " to be processed\n");
        display('Still ' . count($rulesets) . " to be processed\n");

        $this->finish();
    }

    public function finalMark(array $finalMark): void {
        $sqlite = new \Sqlite3($this->config->dump);
        $sqlite->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

        $values = array();
        foreach($finalMark as $key => $value) {
            $values[] = "(null, '$key', '$value')";
        }

        $sqlite->query('REPLACE INTO hash VALUES ' . implode(', ', $values));
    }

    private function processResultsRuleset(array $ruleset, array $counts = array()): int {
        $analyzers = $this->rulesets->getRulesetsAnalyzers($ruleset);

        return $this->processMultipleResults($analyzers, $counts);
    }

    private function processResultsList(array $rulesetList, array $counts = array()): int {
        return $this->processMultipleResults($rulesetList, $counts);
    }

    private function processMultipleResults(array $analyzers, array $counts): int {
        $specials = array('Php/Incompilable',
                          'Composer/UseComposer',
                          'Composer/UseComposerLock',
                          'Composer/Autoload',
                          );
        $diff = array_intersect($specials, $analyzers);
        if (!empty($diff)) {
            $this->dump->removeResults($diff);
            foreach($diff as $d) {
                $this->processResults($d, $counts[$d] ?? -3);
            }
            $analyzers = array_diff($analyzers, $diff);
        }

        $saved = 0;
        $docs = exakat('docs');
        $severities = array();
        $readCounts = array(array());

        $skipAnalysis      = array();
        $ignore_dirs       = array();
        $ignore_namespaces = array();
        $filters           = array();
        $analyzers = array_filter($analyzers, function (string $s): bool {
            return substr($s, 0, 9) !== 'Complete/' &&
                   (substr($s, 0, 5) !== 'Dump/'     ||
                    $s === 'Dump/CouldBeAConstant')
                   ;
        });
        // Remove analysis that are not exported via dump
        foreach($analyzers as $id => $analyzer) {
            $a = $this->rulesets->getInstance($analyzer);
            if ($a instanceof AnalyzerDump) {
                unset($analyzers[$id]);
                $skipAnalysis[] = $analyzer;
            }

            $filters[] = $a->getFilters();

            if (!empty($this->config->{$analyzer}['ignore_dirs'])) {
                $ignore_dirs[$analyzer] = is_array( $this->config->{$analyzer}['ignore_dirs']) ? $this->config->{$analyzer}['ignore_dirs'] : array($this->config->{$analyzer}['ignore_dirs']);
                foreach($ignore_dirs[$analyzer] as &$ignore) {
                    if ($ignore[0] === '/') {
                        $ignore = "/$ignore.*/";
                    } else {
                        $ignore = "/.*$ignore.*/";
                    }
                }
                unset($ignore);
            }

            if (isset($this->config->{$analyzer}['ignore_namespaces'])) {
                $ignore_namespaces[$analyzer] = $ignore_namespaces[$analyzer] ?? array();
                foreach($this->config->{$analyzer}['ignore_namespaces'] as $ignore) {
                    if ($ignore[0] === '/') {
                        $ignore_namespaces[$analyzer][] = '/' . addslashes($ignore) . '.*/i';
                    } else {
                        $ignore_namespaces[$analyzer][] = '/.*' . addslashes($ignore) . '.*/i';
                    }
                }
            }
        }
        $this->dump->removeResults($analyzers);
        $filters = array_merge(...$filters);

        $chunks = array_chunk($analyzers, 100);
        // Gremlin only accepts chunks of 255 maximum

        foreach($chunks as $id => $chunk) {
            $query = $this->newQuery('processMultipleResults ' . $id);
            $query->atomIs('Analysis', Analyzer::WITHOUT_CONSTANTS)
                  ->is('analyzer', $chunk)
                  ->savePropertyAs('analyzer', 'analyzer')
                  ->outIs('ANALYZED')
                  ->atomIsNot('Noresult')
                  ->initVariable('ligne',        'it.get().value("line")')
                  ->initVariable('fullcode_',    'it.get().value("fullcode")')
                  ->initVariable('file',         '"None"')
                  ->initVariable('theFunction',  '""')
                  ->initVariable('theClass',     '""')
                  ->initVariable('theNamespace', '""')
            ->raw(<<<GREMLIN
where( __.until( hasLabel("Project") ).repeat( 
    __.in($this->linksDown)
      .sideEffect{ if (it.get().label() in ["Function", "Closure", "Arrowfunction", "Magicmethod", "Method"]) { theFunction = it.get().value("fullcode")} }
      .sideEffect{ if (it.get().label() in ["Class", "Trait", "Interface", "Classanonymous"]) { theClass = it.get().value("fullcode")} }
      .sideEffect{ if (it.get().label() == "Namespace") { theNamespace = it.get().value("fullnspath"); } }
      .sideEffect{ if (it.get().label() == "File") { file = it.get().value("fullcode")} }
       ).fold()
)
GREMLIN
)
            ->getVariable(array('fullcode_', 'file', 'ligne', 'theNamespace', 'theClass', 'theFunction', 'analyzer'),
                          array('fullcode',  'file', 'line' , 'namespace',    'class',    'function',    'analyzer'));
            $query->prepareRawQuery();
            try {
                $res = $this->gremlin->query($query->getQuery(), $query->getArguments())->toArray();
            } catch (\Throwable $e) {
                $rows = explode(PHP_EOL, $e->getMessage());
                $this->log->log('error while dumping data' . PHP_EOL . $rows[0]);

                continue;
            }

            $toDump = array();
            foreach($res as $result) {
                if (empty($result)) {
                    continue;
                }

                foreach($filters as $filter) {
                    if (!$filter->filterFile($result)) {
                        display ('Skipping ' . $result['file'] . ' (' . get_class($filter) . ') ' . PHP_EOL);
                        --$counts[$result['analyzer']];
                        continue 2;
                    }
                }

                if (isset($severities[$result['analyzer']])) {
                    $severity = $severities[$result['analyzer']];
                } else {
                    $severity = $docs->getDocs($result['analyzer'])['severity'];
                    $severities[$result['analyzer']] = $severity;
                }

                $toDump[] = array($result['fullcode'],
                                  $result['file'],
                                  $result['line'],
                                  $result['namespace'],
                                  $result['class'],
                                  $result['function'],
                                  $result['analyzer'],
                                  $severity,
                                  );
            }

            $readCounts[] = $this->dump->addResults($toDump);
        }
        $readCounts = array_merge(...$readCounts);

        $this->log->log(implode(', ', $analyzers) . " : dumped $saved");

        $error = 0;
        $emptyResults = $skipAnalysis;
        foreach($analyzers as $class) {
            if (!isset($counts[$class]) || $counts[$class] < 0) {
                $emptyResults[] = $class;
                continue;
            }

            if ($counts[$class] === 0 && !isset($readCounts[$class])) {
                display("No results saved for $class\n");
                $emptyResults[] = $class;
            } elseif ($counts[$class] === ($readCounts[$class] ?? 0)) {
                display("All $counts[$class] results saved for $class\n");
            } else {
                assert(($counts[$class] ?? 0) === ($readCounts[$class] ?? 0), "'results were not correctly dumped in $class : $readCounts[$class]/$counts[$class]");
                ++$error;
            }
        }

        $this->dump->addEmptyResults($emptyResults);

        return $error;
    }

    private function processResults(string $class, int $count): void {
        $this->log->log( "$class : $count\n");
        // No need to go further
        if ($count <= 0) {
            $saved = $this->dump->addEmptyResults(array($class));
            return;
        }

        $analyzer = $this->rulesets->getInstance($class, $this->gremlin, $this->config);
        $res = $analyzer->getDump();

        $docs = exakat('docs');
        $severity = $docs->getDocs($class)['severity'];

        $toDump = array();
        foreach($res as $result) {
            if (empty($result)) {
                continue;
            }

            $toDump[] = array($result['fullcode'],
                              $result['file'],
                              $result['line'],
                              $result['namespace'],
                              $result['class'],
                              $result['function'],
                              $class,
                              $severity,
                              );
        }

        if (empty($toDump)) {
            $this->dump->addEmptyResults(array($class));
            return;
        }

        $saved = $this->dump->addResults($toDump);
        $saved = $saved[$class];

        $this->log->log("$class : dumped " . $saved);

        if ($count === $saved) {
            display("All $saved results saved for $class\n");
        } else {
            assert($count === $saved, "'results were not correctly dumped in $class : $saved/$count");
            display("$saved results saved, $count expected for $class\n");
        }
    }

    private function finish(): void {
        $this->dump->close();
        $this->removeSnitch();
    }

    private function collectHashAnalyzer(): void {
        $tables = array('hashAnalyzer',
                       );
        $this->dump->collectTables($tables);
    }

    private function collectStructures(): void {

        $this->dump->cleanTable('phpdoc');
        $this->dump->cleanTable('attributes');
        $this->dump->cleanTable('typehints');

        $namespacesId = $this->collectStructures_namespaces();

        $MAX_LOOPING = Analyzer::MAX_LOOPING;
        $query = $this->newQuery('cit classes');
        $query->atomIs('Class', Analyzer::WITHOUT_CONSTANTS)

              ->initVariable('extendList',     '""')
              ->initVariable('implementList',  '[]')
              ->initVariable('useList',        '[]')
              ->initVariable('usesOptions',    '[]')
              ->initVariable('lines',          '[it.get().value("line")]')
              ->initVariable('file',           "''")

              ->collectOut('phpdoc',     'PHPDOC')
              ->collectOut('attributes', 'ATTRIBUTE')

              ->raw(<<<GREMLIN
 where(__.outE().hasLabel("EXTENDS").not(has("extra", true)).outV().optional(__.out("DEFINITION").where(__.in("USE"))).sideEffect{ extendList = it.get().value("fullnspath"); }.fold() )
.where(__.outE().hasLabel("IMPLEMENTS").not(has("extra", true)).outV().optional(__.out("DEFINITION").where(__.in("USE"))).sideEffect{ implementList.push( it.get().value("fullnspath"));}.fold() )
.where(__.outE().hasLabel("USE").not(has("extra", true)).outV().hasLabel("Usetrait").out("USE").optional(__.out("DEFINITION").where(__.in("USE"))).sideEffect{ useList.push( it.get().value("fullnspath"));}.fold() )
.where(__.outE().hasLabel("USE").not(has("extra", true)).outV().hasLabel("Usetrait").out("BLOCK").out("EXPRESSION").sideEffect{ usesOptions.push( it.get().value("fullcode"));}.fold() )
.where(__.out("METHOD", "MAGICMETHOD", "USE", "PPP", "CONST").emit().repeat( __.out($this->linksDown)).times($MAX_LOOPING).has("line").sideEffect{ lines.add(it.get().value("line")); }.fold())
.where(__.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV() ).until(hasLabel("File")).hasLabel("File").sideEffect{ file = it.get().value("fullcode"); }.fold() )
GREMLIN
             )->selectMap(array(
         'id'             => '""',
         'fullnspath'     => 'it.get().value("fullnspath")',
         'name'           => 'it.get().vertices(OUT, "NAME").next().value("fullcode")',
         'namespace'      => '1',
         'type'           => '"class"',
         'abstract'       => 'it.get().properties("abstract").any()',
         'final'          => 'it.get().properties("final").any()',
         'phpdoc'         => 'phpdoc',
         'begin'          => 'lines.min()',
         'end'            => 'lines.max()',
         'file'           => 'file',
         'line'           => 'it.get().value("line")',
         'extends'        => 'extendList',
         'implements'     => 'implementList',
         'uses'           => 'useList.unique()',
         'usesOptions'    => 'usesOptions.join(";")',
         'attributes'     => 'attributes',
             ));

        $query->prepareRawQuery();
        $classes = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $total = 0;

        $cit            = array();
        $toAttributes   = array();
        $toPhpdoc       = array();
        $citId          = array();
        $cit_implements = array();
        $cit_extends    = array();
        $cit_use        = array();

        foreach($classes as $row) {
            $namespace = preg_replace('#\\\\[^\\\\]*?$#is', '', $row['fullnspath']) . '\\';

            if (isset($namespacesId[$namespace])) {
                $namespaceId = $namespacesId[$namespace];
            } else {
                $namespaceId = 1;
            }

            $cit_implements[$row['line'] . $row['fullnspath']] = $row['implements'];
            unset($row['implements']);
            $cit_use[$row['line'] . $row['fullnspath']] = array('uses'    => $row['uses'],
                                                                'options' => $row['usesOptions'],
                                                                );

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            'class',
                                            $this->citCount,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'class',
                                        $this->citCount,
                                        $phpdoc
                                        );
                }
            }

            unset($row['uses']);
            unset($row['usesOptions']);
            unset($row['attributes']);
            unset($row['phpdoc']);
            $citId[$row['line'] . $row['fullnspath']] = ++$this->citCount;
            unset($row['fullnspath']);
            $row['namespace'] = $namespaceId;
            $cit[] = $row;

            ++$total;
        }
        display("$total classes\n");

        // Interfaces
        $query = $this->newQuery('cit interfaces');
        $query->atomIs('Interface', Analyzer::WITHOUT_CONSTANTS)
              ->initVariable('extendList',  '[]')
              ->initVariable('lines',       '[it.get().value("line")]')
              ->initVariable('file',        '""')

              ->collectOut('phpdoc',     'PHPDOC')

              ->raw(<<<GREMLIN
 where(__.outE().hasLabel("EXTENDS").not(has("extra", true)).outV().sideEffect{ extendList.add(it.get().value("fullnspath")); }.fold() )
.where( __.out("METHOD", "MAGICMETHOD", "CONST").emit().repeat( __.out($this->linksDown)).times($MAX_LOOPING).has("line").sideEffect{ lines.add(it.get().value("line")); }.fold())
.where( __.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File")).hasLabel("File").sideEffect{ file = it.get().value("fullcode"); }.fold() )
GREMLIN
              )
              ->selectMap(array(
         'id'         => '""',
         'fullnspath' => 'it.get().value("fullnspath")',
         'name'       => 'it.get().vertices(OUT, "NAME").next().value("fullcode")',
         'namespace'  => '1',
         'type'       => '"interface"',
         'abstract'   => '0',
         'final'      => '0',
         'phpdoc'     => 'phpdoc',
         'begin'      => 'lines.min()',
         'end'        => 'lines.max()',
         'file'       => 'file',
         'line'       => 'it.get().value("line")',
         'extending'  => 'extendList',
              ));

        $query->prepareRawQuery();
        $interfaces = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $total      = 0;
        foreach($interfaces as $row) {
            $namespace = preg_replace('#\\\\[^\\\\]*?$#is', '', $row['fullnspath']) . '\\';

            if (isset($namespacesId[$namespace])) {
                $namespaceId = $namespacesId[$namespace];
            } else {
                $namespaceId = 1;
            }

            $cit_extends[$row['line'] . $row['fullnspath']] = $row['extending'];
            unset($row['extending']);

            $citId[$row['line'] . $row['fullnspath']] = ++$this->citCount;
            $row['namespace'] = $namespaceId;
            $row['extends'] = '';

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'interface',
                                        $this->citCount,
                                        $phpdoc
                                        );
                }
            }

            unset($row['fullnspath']);
            unset($row['phpdoc']);

            $cit[] = $row;

            ++$total;
        }
        display("$total interfaces\n");

        // Enumerations
        $query = $this->newQuery('cit enums');
        $query->atomIs('Enum', Analyzer::WITHOUT_CONSTANTS)
              ->initVariable('lines',       '[it.get().value("line")]')
              ->initVariable('file',        '""')
              ->initVariable('type',        '""')

              ->collectOut('phpdoc',     'PHPDOC')
              ->collectOut('attributes', 'ATTRIBUTE')

              ->raw(<<<GREMLIN
 where(__.outE().hasLabel("EXTENDS").not(has("extra", true)).outV().sideEffect{ extendList.add(it.get().value("fullnspath")); }.fold() )
.where( __.out("METHOD", "MAGICMETHOD", "CONST").emit().repeat( __.out($this->linksDown)).times($MAX_LOOPING).has("line").sideEffect{ lines.add(it.get().value("line")); }.fold())
.where( __.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File")).hasLabel("File").sideEffect{ file = it.get().value("fullcode"); }.fold() )
.where(__.out("TYPEHINT").sideEffect{ type = it.get().value("fullcode"); }.fold() )
GREMLIN
             )
             ->selectMap(array(
         'id'         => '""',
         'fullnspath' => 'it.get().value("fullnspath")',
         'name'       => 'it.get().vertices(OUT, "NAME").next().value("fullcode")',
         'namespace'  => '1',
         'type'       => '"enum"',
         'abstract'   => '0',
         'final'      => '0',
         'phpdoc'     => 'phpdoc',
         'begin'      => 'lines.min()',
         'end'        => 'lines.max()',
         'file'       => 'file',
         'line'       => 'it.get().value("line")',
         'extends'    => 'type',
         'attributes' => 'attributes',
             ));

        $query->prepareRawQuery();
        $enumerations = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $total = 0;
        $toAttributes   = array();
        foreach($enumerations as $row) {
            $namespace = preg_replace('#\\\\[^\\\\]*?$#is', '', $row['fullnspath']) . '\\';

            if (isset($namespacesId[$namespace])) {
                $namespaceId = $namespacesId[$namespace];
            } else {
                $namespaceId = 1;
            }

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            'enum',
                                            $this->citCount,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'enum',
                                        $this->citCount,
                                        $phpdoc
                                        );
                }
            }

            $citId[$row['line'] . $row['fullnspath']] = ++$this->citCount;
            unset($row['fullnspath']);
            unset($row['attributes']);
            unset($row['phpdoc']);
            $row['namespace'] = $namespaceId;

            $cit[] = $row;

            ++$total;
        }
        display("$total enums\n");

        // Traits
        $query = $this->newQuery('cit traits');
        $query->atomIs('Trait', Analyzer::WITHOUT_CONSTANTS)
              ->initVariable('useList',      '[]')
              ->initVariable('usesOptions',  '[]')
              ->initVariable('lines',        '[it.get().value("line")]')
              ->initVariable('file',         '""')

              ->collectOut('phpdoc',     'PHPDOC')

              ->raw(<<<GREMLIN
 where(__.outE().hasLabel("USE").not(has("extra", true)).outV().hasLabel("Usetrait").out("USE").sideEffect{ useList.push( it.get().value("fullnspath"));}.fold() )
.where(__.outE().hasLabel("USE").not(has("extra", true)).outV().hasLabel("Usetrait").out("BLOCK").out("EXPRESSION").sideEffect{ usesOptions.push( it.get().value("fullcode"));}.fold() )
.where( __.out("METHOD", "MAGICMETHOD", "CONST", "USE", "PPP").emit().repeat( __.out($this->linksDown)).times($MAX_LOOPING).has("line").sideEffect{ lines.add(it.get().value("line")); }.fold())
.where( __.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File")).hasLabel("File").sideEffect{ file = it.get().value("fullcode"); }.fold() )
GREMLIN
)           ->selectMap(array(
         'id'          => '""',
         'fullnspath'  => 'it.get().value("fullnspath")',
         'name'        => 'it.get().vertices(OUT, "NAME").next().value("fullcode")',
         'namespace'   => '1',
         'type'        => '"trait"',
         'abstract'    => '0',
         'final'       => '0',
         'phpdoc'      => 'phpdoc',
         'begin'       => 'lines.min()',
         'end'         => 'lines.max()',
         'file'        => 'file',
         'line'        => 'it.get().value("line")',
         'extends'     => '""',
         'implements'  => '[]',
         'uses'        => 'useList.unique()',
         'usesOptions' => 'usesOptions.join(";")',
));


        $query->prepareRawQuery();
        $traits = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $total = 0;
        foreach($traits as $row) {
            $namespace = preg_replace('#\\\\[^\\\\]*?$#is', '', $row['fullnspath']) . '\\';

            if (isset($namespacesId[$namespace])) {
                $namespaceId = $namespacesId[$namespace];
            } else {
                $namespaceId = 1;
            }

            $row['implements'] = array(); // always empty

            unset($row['implements']);
            $cit_use[$row['line'] . $row['fullnspath']] = array('uses'    => $row['uses'],
                                                                'options' => $row['usesOptions'],
                                                                );

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'trait',
                                        $this->citCount,
                                        $phpdoc
                                        );
                }
            }

            unset($row['uses']);
            unset($row['usesOptions']);
            unset($row['phpdoc']);
            $citId[$row['line'] . $row['fullnspath']] = ++$this->citCount;
            unset($row['fullnspath']);
            $row['namespace'] = $namespaceId;

            $cit[] = $row;

            ++$total;
        }
        display("$total traits\n");

        if (!empty($cit)) {
            foreach($cit as &$aCit) {
                if (empty($aCit['extends'])) {
                    continue;
                }

                $citIds = preg_grep('/^\d+' . preg_quote($aCit['extends'], '/') . '$/i', array_keys($citId));

                if (!empty($citIds)) {
                    $aCit['extends'] = (int) $citId[array_pop($citIds)];
                }
            }
            unset($aCit);
            $this->storeToDumpArray('cit', $cit);

            $toDump = array();
            foreach($cit_implements as $id => $impl) {
                foreach($impl as $implements) {
                    $citIds = preg_grep('/^\d+' . preg_quote(mb_strtolower($implements)) . '$/', array_keys($citId));

                    if (empty($citIds)) {
                        $toDump[] = array('', $citId[$id], $implements, 'implements', '');
                    } else {
                        // Here, we are missing the one that are not found
                        foreach($citIds as $c) {
                            $toDump[] = array('', $citId[$id], $citId[$c], 'implements', '');
                        }
                    }
                }
            }

            $total = $this->storeToDumpArray('cit_implements', $toDump);
            display("$total implements \n");

            $toDump = array();
            foreach($cit_use as $id => $use1) {
                $options = $use1['options'];

                foreach($use1['uses'] as $uses) {
                    $citIds = preg_grep('/^\d+\\\\' . addslashes(mb_strtolower($uses)) . '$/', array_keys($citId));

                    if (empty($citIds)) {
                        $toDump[] = array('',
                                          $citId[$id],
                                          $uses,
                                          'use',
                                          $options,
                                          );
                    } else {
                        // Here, we are missing the one that are not found
                        foreach($citIds as $c) {
                            $toDump[] = array('',
                                              $citId[$id],
                                              $uses,
                                              'use',
                                              $options,
                                              );
                        }
                    }

                    // Options are stored only one for all. PHP doesn't care.
                    $options = '';
                }
            }
            $total = $this->storeToDumpArray('cit_implements', $toDump);
            display("$total uses\n");

            $toDump = array();
            foreach($cit_extends as $id => $extend) {
                foreach($extend as $extends) {
                    $citIds = preg_grep('/^\d+' . preg_quote(mb_strtolower($extends)) . '$/', array_keys($citId));

                    if (empty($citIds)) {
                        $toDump[] = array('',
                                          $citId[$id],
                                          $extends,
                                          'extends',
                                          '',
                                          );
                    } else {
                        // Here, we are missing the one that are not found
                        foreach($citIds as $c) {
                            $toDump[] = array('',
                                              $citId[$id],
                                              $citId[$c],
                                              'extends',
                                              '',
                                              );
                        }
                    }
                }
            }
            $total = $this->storeToDumpArray('cit_implements', $toDump);
            display("$total extends\n");

            $this->storeToDumpArray('attributes', $toAttributes);
            $this->storeToDumpArray('phpdoc'    , $toPhpdoc);
        }

        // Methods
        $query = $this->newQuery('cit methods');
        $query->atomIs(array('Method', 'Magicmethod'), Analyzer::WITHOUT_CONSTANTS)
              ->raw(<<<'GREMLIN'
     coalesce( 
            __.out("BLOCK").out("EXPRESSION").hasLabel("As"),
            __.hasLabel("Method", "Magicmethod")
     )
GREMLIN
)
             ->initVariable('returntype',       '[]')
             ->initVariable('returntype_fnp',   '[]')
             ->initVariable('returntype_type',  'it.get().value("typehint")')

              ->collectOut('phpdoc',     'PHPDOC')
              ->collectOut('attributes', 'ATTRIBUTE')

             ->raw(<<<GREMLIN
      where(
        __.coalesce( 
            __.out("AS").sideEffect{alias = it.get().value("fullcode")}.in("AS")
              .out("NAME").in("DEFINITION").hasLabel("Method", "Magicmethod"), 
            __.sideEffect{ alias = false; }
          )
         .as("method")
         .in("METHOD", "MAGICMETHOD").hasLabel("Class", "Interface", "Trait").has("line").sideEffect{classe = it.get().value("fullnspath"); classline =  it.get().value("line"); }
         .select("method")
         .where( __.sideEffect{ lines = [it.get().value("line")];}
                   .out("BLOCK").out("EXPRESSION")
                   .emit().repeat( __.out($this->linksDown)).times($MAX_LOOPING)
                   .has("line").sideEffect{ lines.add(it.get().value("line")); }
                   .fold()
          )
          .where( __.out('RETURNTYPE').not(hasLabel('Void')).sideEffect{ returntype.add(it.get().value("fullcode"));  
                                                                         returntype_fnp.add(it.get().value("fullnspath"));}.fold())
          .where( __.out('NAME').sideEffect{ name = it.get().value("fullcode")}.fold())
          .map{ 

    if (alias == false) {
        signature = it.get().value("fullcode");
    } else {
        signature = it.get().value("fullcode").replaceFirst("function .*?\\\\(", "function "+alias+"(" );
    }
        }
      ) 
GREMLIN
)
            ->selectMap(array(
         'signature'       => 'signature',
         'name'            => 'name',
         'abstract'        => 'it.get().properties("abstract").any()',
         'final'           => 'it.get().properties("final").any()',
         'static'          => 'it.get().properties("static").any()',
         'reference'       => 'it.get().properties("reference").any()',
         'returntype'      => 'returntype',
         'returntype_fnp'  => 'returntype_fnp',
         'returntype_type' => 'returntype_type',
         'visibility'      => 'it.get().value("visibility")',
         'class'           => 'classe',
         'phpdoc'          => 'phpdoc',
         'begin'           => 'lines.min()',
         'end'             => 'lines.max()',
         'classline'       => 'classline',
         'attributes'      => 'attributes',
            ));

        $query->prepareRawQuery();
        $methods = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump        = array();
        $toAttributes  = array();
        $toTypehints   = array();
        $toPhpdoc      = array();
        foreach($methods as $row) {
            $row['visibility'] = $row['visibility'] === 'none' ? '' : $row['visibility'];

            if (!isset($citId[$row['classline'] . $row['class']])) {
                continue;
            }
            $methodId = $row['class'] . '::' . mb_strtolower($row['name']);
            if (isset($this->methodIds[$methodId])) {
                continue; // skip double
            }
            $this->methodIds[$methodId] = ++$this->methodCount;

            assert(isset($citId[$row['classline'] . $row['class']]));

            $toDump[] = array($this->methodCount,
                             $row['name'],
                             $citId[$row['classline'] . $row['class']],
                             (int) $row['static'],
                             (int) $row['final'],
                             (int) $row['abstract'],
                             (int) $row['reference'],
                             $row['visibility'],
                             $row['returntype_type'],
                             (int) $row['begin'],
                             (int) $row['end']
                             );
            ++$total;

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            'method',
                                            $this->methodCount,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'method',
                                        $this->methodCount,
                                        $phpdoc
                                        );
                }
            }

            if (!empty($row['returntype'])) {
                foreach($row['returntype'] as $id => $typehint) {
                    $toTypehints[] = array(0,
                                           'method',
                                            $this->methodCount,
                                            $typehint,
                                            $row['returntype_fnp'][$id],
                                           );
                }
            }

        }
        $this->dump->cleanTable('methods');
        $this->storeToDumpArray('attributes', $toAttributes);
        $this->storeToDumpArray('typehints',  $toTypehints);
        $this->storeToDumpArray('phpdoc',     $toPhpdoc);
        $total = $this->storeToDumpArray('methods', $toDump);

        // Parameters
        $query = $this->newQuery('Method parameters');
        $query->atomIs('Parameter', Analyzer::WITHOUT_CONSTANTS)
              ->inIs('ARGUMENT')
              ->atomIs(array('Method', 'Magicmethod'), Analyzer::WITHOUT_CONSTANTS)
              ->raw(<<<'GREMLIN'
where( __.out('NAME').sideEffect{ methode = it.get().value("fullcode").toString().toLowerCase() }.fold())
GREMLIN
)
             ->inIs(array('METHOD', 'MAGICMETHOD'))
             ->atomIs(array('Class', 'Interface', 'Trait'), Analyzer::WITHOUT_CONSTANTS)
             ->savePropertyAs('fullnspath', 'classe')
             ->savePropertyAs('line', 'classline')
             ->back('first')

             ->initVariable('ligne',          'it.get().value("line")')
             ->initVariable('init',           '""')
             ->initVariable('expression',     '0')
             ->initVariable('hasDefault',     '0')
             ->initVariable('typehint_hints', '[]')
             ->initVariable('typehint_fnp',   '[]')
             ->initVariable('typehint_type',  'it.get().value("typehint")')

             ->collectOut('phpdoc',     'PHPDOC')
             ->collectOut('attributes', 'ATTRIBUTE')
             ->collectOut('name',       'NAME'      , 'fullcode')

             ->raw(<<<'GREMLIN'
 where( __.out('TYPEHINT').not(hasLabel('Void')).not(__.in('DEFAULT')).sideEffect{ typehint_hints.add(it.get().value("fullcode")); typehint_fnp.add(it.get().value("fullnspath"));}.fold())
.where( __.out('DEFAULT').not(where(__.in("RIGHT"))).sideEffect{ init = it.get().value("fullcode"); if (it.get().label() in ["Integer", "Null", "Float", "Boolean", "String", "Heredoc"]) { hasDefault = 1; } else { hasDefault = 1; expression = 1; }}.fold())
GREMLIN
)
              ->selectMap(array(
         'name'          => 'name[0]',
         'rank'          => 'it.get().value("rank")',
         'variadic'      => 'it.get().properties("variadic").any()',
         'reference'     => 'it.get().properties("reference").any()',
         'classe'        => 'classe',
         'methode'       => 'methode',
         'line'          => 'ligne',
         'classline'     => 'classline',
         'init'          => 'init',
         'expression'    => 'expression',
         'hasDefault'    => 'hasDefault',
         'typehint'      => 'typehint_hints',
         'typehint_fnp'  => 'typehint_fnp',
         'typehint_type' => 'typehint_type',
         'phpdoc'        => 'phpdoc',
         'attributes'    => 'attributes',
));


        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump       = array();
        $toAttributes = array();
        $toTypehints  = array();
        foreach($result->toArray() as $row) {
            assert(isset($this->methodIds[$row['classe'] . '::' . mb_strtolower($row['methode'])]));
            $toDump[] = array(++$this->argumentsId,
                              $row['name'],
                              (int) $citId[$row['classline'] . $row['classe']],
                              $this->methodIds[$row['classe'] . '::' . mb_strtolower($row['methode'])],
                              (int) $row['rank'],
                              (int) $row['reference'],
                              (int) $row['variadic'],
                              $row['init'],
                              $row['expression'],
                              $row['hasDefault'],
                              (int) $row['line'],
                              $row['typehint_type'],
            );

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            'argument',
                                            $this->argumentsId,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'argument',
                                        $this->argumentsId,
                                        $phpdoc
                                        );
                }
            }

            if (!empty($row['typehint'])) {
                foreach($row['typehint'] as $id => $typehint) {
                    $toTypehints[] = array(0,
                                           'argument',
                                            $this->argumentsId,
                                            $typehint,
                                            $row['typehint_fnp'][$id],
                                           );
                }
            }
        }

        $total = $this->storeToDumpArray('arguments', $toDump);
        $this->storeToDumpArray('attributes',  $toAttributes);
        $this->storeToDumpArray('typehints',   $toTypehints);
        $this->storeToDumpArray('phpdoc',      $toPhpdoc);

        // Properties
        $query = $this->newQuery('Properties');
        $query->atomIs('Propertydefinition', Analyzer::WITHOUT_CONSTANTS)
              ->_as('property')
              ->raw(<<<'GREMLIN'
 sideEffect{ 
     b = it.get().value("fullcode").tokenize(' = ');
     name = b[0];
}

GREMLIN
)
              ->initVariable('hasDefault',     '0')
              ->raw(<<<'GREMLIN'
      where( __.out('DEFAULT').hasLabel('Void').sideEffect{ hasDefault = 0;}.fold())
     .where( __.out('DEFAULT').not(where(__.in("RIGHT"))).not(__.hasLabel('Void')).sideEffect{ hasDefault = 1; init = it.get().value("fullcode"); if (it.get().label() in ["Integer", "Null", "Float", "Boolean", "String", "Heredoc"]) { expression = 0; } else { expression = 1;  }}.fold())
GREMLIN)

              ->inIs('PPP')

              ->initVariable('x_static',       'it.get().properties("static").any()')
              ->initVariable('x_visibility',   'it.get().value("visibility")')
              ->initVariable('x_var',          'it.get().value("token") == "T_VAR"')
              ->initVariable('readonly',       'it.get().properties("readonly").any()')
              ->initVariable('file',           '""')
              ->initVariable('expression',     '""')
              ->initVariable('init',           '""')
              ->initVariable('typehint_hints', '[]')
              ->initVariable('typehint_fnp',   '[]')
              ->initVariable('typehint_type',  'it.get().value("typehint")')

              ->collectOut('phpdoc',     'PHPDOC')

              ->raw(<<<'GREMLIN'
      where( __.out('TYPEHINT').not(hasLabel('Void')).not(__.in('DEFAULT')).sideEffect{ typehint_hints.add(it.get().value("fullcode")); typehint_fnp.add(it.get().value("fullnspath"));}.fold())
GREMLIN)

              ->filter(
                  $query->side()
                        ->inIs('PPP')
                        ->atomIs(array('Class', 'Interface', 'Trait'))
                        ->savePropertyAs('fullnspath',    'classe')
                        ->savePropertyAs('line',          'classline')
              )

              ->selectMap(array(
                    'class'         => 'classe',
                    'static'        => 'x_static',
                    'visibility'    => 'x_visibility',
                    'var'           => 'x_var',
                    'line'          => 'it.get().value("line")',
                    'name'          => 'name',
                    'value'         => 'init',
                    'hasDefault'    => 'hasDefault',
                    'expression'    => 'expression',
                    'phpdoc'        => 'phpdoc',
                    'classline'     => 'classline',
                    'readonly'      => 'readonly',
                    'typehint'      => 'typehint_hints',
                    'typehint_fnp'  => 'typehint_fnp',
                    'typehint_type' => 'typehint_type',
                    )
                );

        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump        = array();
        $toPhpdoc      = array();
        $toTypehints   = array();
        foreach($result->toArray() as $row) {
            $row['visibility'] = $row['visibility'] === 'none' ? '' : $row['visibility'];

            // If we haven't found any definition for this class, just ignore it.
            if (!isset($citId[$row['classline'] . $row['class']])) {
                continue;
            }
            $propertyId = $row['class'] . '::' . $row['name'];
            if (isset($this->propertyIds[$propertyId])) {
                continue; // skip double
            }
            $this->propertyIds[$propertyId] = ++$this->propertyCount;

            $toDump[] = array($this->propertyCount,
                              $row['name'],
                              (int) $citId[$row['classline'] . $row['class']],
                              $row['visibility'],
                              $row['var'],
                              (int) $row['static'],
                              (int) $row['readonly'],
                              (int) $row['hasDefault'],
                              $row['value'],
                              $row['expression'],
                              $row['line'],
                              $row['typehint_type'],
            );

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'property',
                                        $this->propertyCount,
                                        $phpdoc
                                        );
                }
            }

            if (!empty($row['typehint'])) {
                foreach($row['typehint'] as $id => $typehint) {
                    $toTypehints[] = array(0,
                                           'property',
                                           $this->propertyCount,
                                           $typehint,
                                           $row['typehint_fnp'][$id],
                                           );
                }
            }
        }

        $total = $this->storeToDumpArray('properties', $toDump);
        $this->storeToDumpArray('phpdoc',    $toPhpdoc);
        $this->storeToDumpArray('typehints', $toTypehints);
        display("$total properties\n");

        // Class Constant
        $query = $this->newQuery('cit constants');
        $query->atomIs(array('Class', 'Classanonymous', 'Interface', 'Enum'), Analyzer::WITHOUT_CONSTANTS)
              ->savePropertyAs('line', 'ligne')
              ->savePropertyAs('line', 'classline')
              ->savePropertyAs('fullnspath', 'classe')
              ->outIs('CONST')
              ->savePropertyAs('visibility', 'visibilite')
              ->outIs('CONST')

              ->collectOut('phpdoc',     'PHPDOC')
              ->collectOut('attributes', 'ATTRIBUTE')

              ->collectOut('name', 'NAME', 'fullcode')
              ->collectOut('valeur', 'VALUE', 'fullcode')

              ->selectMap(array('name'       => 'name[0]',
                                'value'      => 'valeur[0]',
                                'visibility' => 'visibilite',
                                'class'      => 'classe',
                                'phpdoc'     => 'phpdoc',
                                'line'       => 'ligne',
                                'classline'  => 'classline',
                                'attributes' => 'attributes',
                               ));

        $query->prepareRawQuery();
        $classConstants = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump          = array();
        $toAttributes    = array();
        $toPhpdoc        = array();
        foreach($classConstants as $row) {
            $row['visibility'] = $row['visibility'] === 'none' ? '' : $row['visibility'];

            // If we haven't found any definition for this class, just ignore it.
            if (!isset($citId[$row['classline'] . $row['class']])) {
                continue;
            }
            $classConstId = $row['class'] . '::' . $row['name'];
            if (isset($this->classConstIds[$classConstId])) {
                continue; // skip double
            }
            $this->classConstIds[$classConstId] = ++$this->classConstCount;

            $toDump[] = array($this->classConstCount,
                              $row['name'],
                              (int) $citId[$row['classline'] . $row['class']],
                              $row['visibility'],
                              $row['value'],
            );

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            'classconstant',
                                            $this->classConstCount,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'classconstant',
                                        $this->classConstCount,
                                        $phpdoc
                                        );
                }
            }
        }

        $total = $this->storeToDumpArray('classconstants', $toDump);
        $this->storeToDumpArray('attributes', $toAttributes);
        $this->storeToDumpArray('phpdoc',     $toPhpdoc);
        display("$total class constants\n");

        // Cases for enumeration
        $query = $this->newQuery('cit case enumeration');
        $query->atomIs(array('Enum'), Analyzer::WITHOUT_CONSTANTS)
              ->savePropertyAs('line',       'ligne')
              ->savePropertyAs('line',       'classline')
              ->savePropertyAs('fullnspath', 'classe')
              ->outIs('ENUMCASE')

              ->collectOut('phpdoc',     'PHPDOC'                  )
              ->collectOut('name',       'NAME',      'fullcode'   )
              ->collectOut('valeur',     'DEFAULT',   'noDelimiter')
              ->collectOut('attributes', 'ATTRIBUTE', 'fullcode'   )

              ->selectMap( array('name'       => 'name[0]',
                                 'value'      => 'valeur[0]',
                                 'visibility' => "'case'",
                                 'class'      => 'classe',
                                 'phpdoc'     => 'phpdoc',
                                 'line'       => 'ligne',
                                 'classline'  => 'classline',
                                 'attributes' => 'attributes',
                                 ));
        $query->prepareRawQuery();
        $cases = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump     = array();
        $toPhpdoc   = array();
        foreach($cases as $row) {
            // If we haven't found any definition for this class, just ignore it.
            if (!isset($citId[$row['classline'] . $row['class']])) {
                print 'Ignoring ' . $row['classline'] . $row['class'] . PHP_EOL;
                continue;
            }
            $classConstId = $row['class'] . '::' . $row['name'];
            if (isset($this->classConstIds[$classConstId])) {
                continue; // skip double
            }
            $this->classConstIds[$classConstId] = ++$this->classConstCount;

            $toDump[] = array($this->classConstCount,
                              $row['name'],
                              (int) $citId[$row['classline'] . $row['class']],
                              $row['visibility'],
                              $row['value'] ?? 'none',
            );

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'enumcase',
                                        $this->classConstCount,
                                        $phpdoc
                                        );
                }
            }
        }

        $total = $this->storeToDumpArray('classconstants', $toDump);
        $this->storeToDumpArray('phpdoc', $toPhpdoc);
        display("$total enumerations cases\n");

        // Global Constants
        $query = $this->newQuery('Constants define()');
        $query->atomIs('Defineconstant', Analyzer::WITHOUT_CONSTANTS)
              ->collectOut('phpdoc',      'PHPDOC')
              ->initVariable('file',      '""')
              ->initVariable('namespace', '"\\\\"')

              ->raw(<<<'GREMLIN'
 where( 
    __.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File"))
           .coalesce( 
                __.hasLabel("File").sideEffect{ file = it.get().value("fullcode"); },
                __.hasLabel("Namespace").sideEffect{ namespace = it.get().value("fullnspath"); }
                )
           .fold() 
)
GREMLIN
)
              ->filter(
                $query->side()
                     ->outIs('NAME')
                     ->is('constant', true)
                     ->savePropertyAs('fullcode', 'name')
              )
              ->filter(
                $query->side()
                     ->outIs('VALUE')
                     ->is('constant', true)
                     ->savePropertyAs('fullcode', 'v')
                     ->raw(<<<'GREMLIN'
 sideEffect{ if (it.get().label() in ["Integer", "Null", "Float", "Boolean", "String", "Heredoc"]) { 
    expression = 0; 
 } else { 
    expression = 1; 
}}
GREMLIN
)
              )
             ->selectMap(array('name'       => 'name',
                               'value'      => 'v',
                               'namespace'  => 'namespace',
                               'file'       => 'file',
                               'type'       => '"define"',
                               'phpdoc'     => 'phpdoc',
                               'expression' => 'expression',
                              ));
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump       = array();
        $toPhpdoc     = array();
        foreach($result->toArray() as $row) {
            if (isset($namespacesId[$row['namespace'] . '\\'])) {
                $namespaceId = $namespacesId[$row['namespace'] . '\\'];
            } else {
                $namespaceId = 1;
            }

            $toDump[] = array(++$this->constantCount,
                              trim($row['name'], "'\""),
                              $namespaceId,
                              $this->files[$row['file']],
                              $row['value'],
                              $row['type'],
                              $row['expression'],
            );

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'const',
                                        $this->constantCount,
                                        $phpdoc
                                        );
                }
            }
        }
        $total = $this->storeToDumpArray('constants', $toDump);
        $this->storeToDumpArray('phpdoc', $toPhpdoc);
        display("$total global constants\n");

        $query = $this->newQuery('Constants const');
        $query->atomIs('Const', Analyzer::WITHOUT_CONSTANTS)
              ->collectOut('phpdoc',     'PHPDOC')
              ->initVariable('file',      '""')
              ->initVariable('namespace', '"\\\\"')

              ->raw(<<<'GREMLIN'
 where( 
    __.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File"))
           .coalesce( 
                __.hasLabel("File").sideEffect{ file = it.get().value("fullcode"); },
                __.hasLabel("Namespace").sideEffect{ namespace = it.get().value("fullnspath"); }
                )
           .fold() 
)
GREMLIN
)
              ->hasNoIn('CONST') // Not class or interface
              ->outIs('CONST')
              ->atomIs('Constant', Analyzer::WITHOUT_CONSTANTS)
              ->filter(
                $query->side()
                     ->outIs('NAME')
                     ->is('constant', true)
                     ->savePropertyAs('fullcode', 'name')
              )
              ->filter(
                $query->side()
                     ->outIs('VALUE')
                     ->is('constant', true)
                     ->savePropertyAs('fullcode', 'v')
                     ->raw('sideEffect{ if (it.get().label() in ["Integer", "Null", "Float", "Boolean", "String", "Heredoc"]) { expression = 0; } else { expression = 1; }}')
              )

              ->selectMap(array('name'       => 'name',
                                'value'      => 'v',
                                'namespace'  => 'namespace',
                                'file'       => 'file',
                                'type'       => '"const"',
                                'phpdoc'     => 'phpdoc',
                                'expression' => 'expression',
                           ));
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump   = array();
        $toPhpdoc = array();
        foreach($result->toArray() as $row) {
            $toDump[] = array(++$this->constantCount,
                              $row['name'],
                              $namespacesId[$row['namespace'] . '\\'] ?? 1,
                              $this->files[$row['file']],
                              $row['value'],
                              $row['type'],
                              $row['expression']
                            );

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'const',
                                        $this->constantCount,
                                        $phpdoc
                                        );
                }
            }
        }

        $total = $this->storeToDumpArray('constants', $toDump);
        $this->storeToDumpArray('phpdoc', $toPhpdoc);
        display("$total const constants\n");

        // Collect Functions
        // Functions
        $query = $this->newQuery('Functions');
        $query->atomIs(array('Function', 'Closure', 'Arrowfunction'), Analyzer::WITHOUT_CONSTANTS)

              ->initVariable('lines',          '[it.get().value("line")]')
              ->initVariable('x_reference',    'it.get().properties("reference").any()')
              ->initVariable('x_fullnspath',   'it.get().value("fullnspath");')
              ->initVariable('returntype_fnp', '[]')
              ->initVariable('returntype',     '[]')
              ->initVariable('returntype_type','it.get().value("typehint")')

              ->collectOut('phpdoc',     'PHPDOC')
              ->collectOut('attributes', 'ATTRIBUTE')
              ->collectOut('name',       'NAME',      'fullcode')

              ->initVariable('file',      '""')
              ->initVariable('namespace', '"\\\\"')

              ->raw(<<<GREMLIN
 where( 
    __.out("BLOCK").has("line").sideEffect{ lines.add(it.get().value("line")); }
      .out("EXPRESSION").emit().repeat( __.out({$this->linksDown})).times($MAX_LOOPING).has("line")
      .sideEffect{ lines.add(it.get().value("line")); }
      .fold()
 )
.where( __.out('RETURNTYPE').not(hasLabel('Void')).sideEffect{ returntype.add(it.get().value("fullcode"));returntype_fnp.add(it.get().value("fullnspath"));}.fold())
GREMLIN
)
              ->raw(<<<'GREMLIN'
 where( 
    __.in().emit().repeat( __.inE().not(hasLabel("DEFINITION")).outV()).until(hasLabel("File"))
           .coalesce( 
                __.hasLabel("File").sideEffect{ file = it.get().value("fullcode"); },
                __.hasLabel("Namespace").sideEffect{ namespace = it.get().value("fullnspath"); }
                )
           .fold() 
)
GREMLIN
)
              ->selectMap(array(
      'name'             => 'name[0]',
      'type'             => 'it.get().label().toString().toLowerCase()',
      'line'             => 'it.get().value("line")',
      'file'             => 'file',
      'namespace'        => 'namespace',
      'fullnspath'       => 'x_fullnspath',
      'reference'        => 'x_reference',
      'returntype'       => 'returntype',
      'returntype_fnp'   => 'returntype_fnp',
      'returntype_type'  => 'returntype_type',
      'begin'            => 'lines.min()',
      'end'              => 'lines.max()',
      'phpdoc'           => 'phpdoc',
      'attributes'       => 'attributes',
));
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump       = array();
        $toAttributes = array();
        $toTypehints  = array();
        $toPhpdoc     = array();
        $unique       = array();
        foreach($result->toArray() as $row) {
            if (isset($unique[$row['name'] . $row['line']])) {
                continue;  // Skipping double definitions until we can differentiate them.
            }
            $unique[$row['name'] . $row['line']] = 1;

            if (strpos($row['fullnspath'], '@') === false) {
                $this->methodIds[$row['fullnspath']] = ++$this->methodCount;
                $n = $row['namespace'];
                if ($n[-1] !== '\\') {
                    $n .= '\\';
                }
                $ns = preg_grep('%^' . addslashes($n) . '$%i', array_keys($namespacesId));
                $k = array_pop($ns);

                $ns = $namespacesId[$k];
            } else {
                $this->methodIds[$row['fullnspath']] = ++$this->methodCount;
                // case of closure or arrow function
                $ns = '';
            }

            $toDump[] = array($this->methodCount,
                              $row['name'] ?? '',
                              $row['type'],
                              $ns,
                              $row['returntype_type'],
                              (int) $row['reference'],
                              $this->files[$row['file']] ?? '',
                              (int) $row['begin'],
                              (int) $row['end'],
                              );

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            $row['type'],
                                            $this->methodCount,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'function',
                                        $this->methodCount,
                                        $phpdoc
                                        );
                }
            }

            if (!empty($row['returntype'])) {
                foreach($row['returntype'] as $id => $typehint) {
                    $toTypehints[] = array(0,
                                           'function',
                                           $this->methodCount,
                                           $typehint,
                                           $row['returntype_fnp'][$id],
                                           );
                }
            }
        }

        $this->dump->cleanTable('functions');
        $total = $this->storeToDumpArray('functions', $toDump);
        $this->storeToDumpArray('typehints',  $toTypehints);
        $this->storeToDumpArray('phpdoc',     $toPhpdoc);
        $this->storeToDumpArray('attributes', $toAttributes);
        display("$total functions\n");

        // Functions parameters
        $query = $this->newQuery('Function parameters');
        $query->atomIs('Parameter', Analyzer::WITHOUT_CONSTANTS)

              ->initVariable('init',           '""')
              ->initVariable('hasDefault',     '0')
              ->initVariable('expression',     '""')
              ->initVariable('typehint_hints', '[]')
              ->initVariable('typehint_fnp',   '[]')
              ->initVariable('typehint_type',  'it.get().value("typehint")')

              ->collectOut('phpdoc',     'PHPDOC')
              ->collectOut('attributes', 'ATTRIBUTE')

              ->inIs('ARGUMENT')
              ->atomIs(array('Function', 'Closure', 'Arrowfunction'), Analyzer::WITHOUT_CONSTANTS)
              ->raw(<<<'GREMLIN'
where( __.sideEffect{ fonction = it.get().label().toString().toLowerCase(); 
                      fullnspath = it.get().value("fullnspath");  
                    }.fold() 
      )
.where( __.out('NAME')
         .sideEffect{ fonction = it.get().value("fullcode").toString().toLowerCase();}
         .fold()
     )
.sideEffect{ classe = it.get().value("fullnspath")}

.select('first')

.where( __.out('NAME').sideEffect{ name = it.get().value("fullcode")}.fold())
.where( __.out('TYPEHINT').not(hasLabel('Void')).not(__.in('DEFAULT')).sideEffect{ typehint_hints.add(it.get().value("fullcode")); typehint_fnp.add(it.get().value("fullnspath"));}.fold())
.where( __.out('DEFAULT').hasLabel('Void').sideEffect{ hasDefault = 0;}.fold())
.where( __.out('DEFAULT').not(where(__.in("RIGHT"))).not(hasLabel('Void')).sideEffect{ hasDefault = 1;}.sideEffect{ init = it.get().value("fullcode"); if (! it.get().label() in ["Integer", "Null", "Float", "Boolean", "String", "Heredoc"]) { expression = 1;  }}.fold())
GREMLIN
)
        ->selectMap(array(
         'name'          => 'name',
         'fullnspath'    => 'fullnspath',
         'rank'          => 'it.get().value("rank")',
         'variadic'      => 'it.get().properties("variadic").any()',
         'reference'     => 'it.get().properties("reference").any()',
         'line'          => 'it.get().value("line")',
         'function'      => 'fonction',
         'init'          => 'init',
         'expression'    => 'expression',
         'hasDefault'    => 'hasDefault',
         'typehint'      => 'typehint_hints',
         'typehint_fnp'  => 'typehint_fnp',
         'typehint_type' => 'typehint_type',
         'phpdoc'        => 'phpdoc',
         'attributes'    => 'attributes',
        ));

        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        $toDump       = array();
        $toPhpdoc     = array();
        $toAttributes = array();
        $toTypehints  = array();
        foreach($result->toArray() as $row) {
            // Those were skipped in the previous loop
            if (!isset($this->methodIds[$row['fullnspath']])) {
                continue;
            }

            $toDump[] = array( ++$this->argumentsId,
                               $row['name'],
                               0,
                               $this->methodIds[$row['fullnspath']],
                               (int) $row['rank'],
                               (int) $row['reference'],
                               (int) $row['variadic'],
                               $row['init'],
                               $row['expression'],
                               $row['hasDefault'],
                               (int) $row['line'],
                               $row['typehint_type'],
            );

            if (!empty($row['attributes'])) {
                foreach($row['attributes'] as $attribute) {
                    $toAttributes[] = array(0,
                                            'argument',
                                            $this->argumentsId,
                                            $attribute);
                }
            }

            if (!empty($row['phpdoc'])) {
                foreach($row['phpdoc'] as $phpdoc) {
                    $toPhpdoc[] = array(0,
                                        'function',
                                        $this->argumentsId,
                                        $phpdoc
                                        );
                }
            }

            if (!empty($row['typehint'])) {
                foreach($row['typehint'] as $id => $typehint) {
                    $toTypehints[] = array(0,
                                           'argument',
                                           $this->argumentsId,
                                           $typehint,
                                           $row['typehint_fnp'][$id],
                                           );
                }
            }
        }

        $total = $this->storeToDumpArray('arguments', $toDump);
        $this->storeToDumpArray('attributes', $toAttributes);
        $this->storeToDumpArray('typehints',  $toTypehints);
        $this->storeToDumpArray('phpdoc',     $toPhpdoc);
        display("$total function arguments\n");
    }

    private function collectStructures_namespaces(): array {
        $query = $this->newQuery('collectStructures_namespaces');
        $query->atomIs('Namespace', Analyzer::WITHOUT_CONSTANTS)
              ->outIs('NAME')
              ->initVariable('name', 'it.get().value("fullcode") == " " ? "\\\\" : "\\\\" + it.get().value("fullcode") + "\\\\"')
              ->getVariable('name')
              ->unique();
        $query->prepareRawQuery();
        $namespaces = $this->gremlin->query($query->getQuery(), $query->getArguments());
        $namespaces->string2Array();

        $this->dump->storeInTable('namespaces', $namespaces);

        $namespacesId = $this->dump->fetchTable('namespaces');
        $namespacesId->map(function (array $x): array { $x['namespace'] = mb_strtolower($x['namespace']); return $x; });
        return $namespacesId->toHash('namespace', 'id');
    }

    private function collectFiles(): void {
        $this->files = $this->dump->fetchTable('files')->toHash('file', 'id');
    }

    private function collectHashCounts(Query $query, string $name): void {
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        $index = $result->toArray()[0] ?? array();

        $toDump = array();
        foreach($index as $number => $count) {
            $toDump[] = array('',
                              $name,
                              $number,
                              $count,
                             );
        }

        if (empty($toDump)) {
            $total = 0;
        } else {
            $total = $this->storeToDumpArray('hashResults', $toDump);
        }

        display( "$name : $total");
    }

    private function collectMissingDefinitions(): void {
        $toDump = array();

        $functioncallCount  = $this->gremlin->query('g.V().hasLabel("Functioncall").count()')[0];
        $functioncallMissed = $this->gremlin->query('g.V().hasLabel("Functioncall")
             .has("token", within("T_STRING", "T_NS_SEPARATOR"))
             .not(where(__.in("DEFINITION")))
             .not( __.has("isPhp", true))
             .not( __.has("isExt", true))
             .where( __.out("NAME").hasLabel("Identifier", "Nsname", "Name"))
        ');
        if ($functioncallMissed === 0) {
            file_put_contents("{$this->config->log_dir}/functions.missing.txt", 'Nothing found');
            $functioncallMissed = 0;
        } else {
            file_put_contents("{$this->config->log_dir}/functions.missing.txt", implode(PHP_EOL, array_column($functioncallMissed->toArray(), 'fullcode')));
            $functioncallMissed = $functioncallMissed->toInt();
        }
        $toDump[] = array('',
                          'functioncall total',
                          $functioncallCount,
                          );
        $toDump[] = array('',
                          'functioncall missed',
                          $functioncallMissed,
                          );

        $methodCount  = $this->gremlin->query('g.V().hasLabel("Methodcall").count()')[0];
        $methodMissed = $this->gremlin->query('g.V().hasLabel("Methodcall")
             .not(where(__.in("DEFINITION")))
             .where( __.out("METHOD").has("token", "T_STRING"))
        ');
        if (is_array($methodMissed)) {
            file_put_contents("{$this->config->log_dir}/methodcall.missing.txt", implode(PHP_EOL, array_column($methodMissed->toArray(), 'fullcode')));
            $methodMissed = $methodMissed->toInt();
        } else {
            file_put_contents("{$this->config->log_dir}/methodcall.missing.txt", 'Nothing found');
            $methodMissed = 0;
        }
        $toDump[] = array('',
                          'methodcall total',
                          $methodCount,
                          );
        $toDump[] = array('',
                          'methodcall missed',
                          $methodMissed,
                          );

        $memberCount  = $this->gremlin->query('g.V().hasLabel("Member").count()')[0];
        $memberMissed = $this->gremlin->query('g.V().hasLabel("Member")
             .not(where(__.in("DEFINITION")))
             .where( __.out("MEMBER").has("token", "T_STRING"))
        ');
        if (is_array($memberMissed)) {
            file_put_contents("{$this->config->log_dir}/members.missing.txt", implode(PHP_EOL, array_column($memberMissed->toArray(), 'fullcode')));
            $memberMissed = $memberMissed->toInt();
        } else {
            file_put_contents("{$this->config->log_dir}/members.missing.txt", 'Nothing found');
            $memberMissed = 0;
        }
        $toDump[] = array('',
                          'member total',
                          $memberCount,
                          );
        $toDump[] = array('',
                          'member missed',
                          $memberMissed,
                          );

        $staticMethodCount  = $this->gremlin->query('g.V().hasLabel("Staticmethodcall").count()')[0];
        $staticMethodMissed = $this->gremlin->query('g.V().hasLabel("Staticmethodcall")
             .not(where(__.in("DEFINITION")))
             .where( __.out("CLASS").hasLabel("Identifier", "Nsname", "Self", "Parent", "Static"))
             .not(where( __.out("CLASS").in("ANALYZED").has("analyzer", "Classes/IsExtClass")))
             .where( __.out("METHOD").has("token", "T_STRING"))
        ');
        if (is_array($staticMethodMissed)) {
            file_put_contents("{$this->config->log_dir}/staticmethodcall.missing.txt", implode(PHP_EOL, array_column($staticMethodMissed->toArray(), 'fullcode')));
            $staticMethodMissed = $staticMethodMissed->count();
        } else {
            file_put_contents("{$this->config->log_dir}/staticmethodcall.missing.txt", 'Nothing found');
            $staticMethodMissed = 0;
        }
        $toDump[] = array('',
                          'static methodcall total',
                          $staticMethodCount,
                          );
        $toDump[] = array('',
                          'static methodcall missed',
                          $staticMethodMissed,
                          );

        $staticConstantCount  = $this->gremlin->query('g.V().hasLabel("Staticonstant").count()')[0];
        $staticConstantMissed = $this->gremlin->query('g.V().hasLabel("Staticonstant")
             .not(where(__.in("DEFINITION")))
             .not(where( __.out("CLASS").in("ANALYZED").has("analyzer", "Classes/IsExtClass")))
             .where( __.out("METHOD").has("token", "T_STRING"))
        ');
        if (is_array($staticConstantMissed)) {
            file_put_contents("{$this->config->log_dir}/staticconstant.missing.txt", implode(PHP_EOL, array_column($staticConstantMissed->toArray(), 'fullcode')));
            $staticConstantMissed = $staticConstantMissed->toInt();
        } else {
            file_put_contents("{$this->config->log_dir}/staticconstant.missing.txt", 'Nothing found');
            $staticConstantMissed = 0;
        }
        $toDump[] = array('',
                          'static constant total',
                          $staticConstantCount,
                          );
        $toDump[] = array('',
                          'static constant missed',
                          $staticConstantMissed,
                          );

        $staticPropertyCount  = $this->gremlin->query('g.V().hasLabel("Staticproperty").count()')[0];
        $staticPropertyMissed = $this->gremlin->query('g.V().hasLabel("Staticproperty")
             .not(where(__.in("DEFINITION")))
             .not(where( __.out("CLASS").in("ANALYZED").has("analyzer", "Classes/IsExtClass")))
             .where( __.out("MEMBER").has("token", "T_VARIABLE"))
        ');
        if (is_array($staticPropertyMissed)) {
            file_put_contents("{$this->config->log_dir}/staticproperty.missing.txt", implode(PHP_EOL, array_column($staticPropertyMissed->toArray(), 'fullcode')));
            $staticPropertyMissed = $staticPropertyMissed->toInt();
        } else {
            file_put_contents("{$this->config->log_dir}/staticproperty.missing.txt", 'Nothing found');
            $staticPropertyMissed = 0;
        }
        $toDump[] = array('',
                          'static property total',
                          $staticPropertyCount,
                          );
        $toDump[] = array('',
                          'static property missed',
                          $staticPropertyMissed,
                          );

        $constantCounts = $this->gremlin->query('g.V().hasLabel("Identifier", "Nsname").count()')[0];
        $constantMissed = $this->gremlin->query('g.V().hasLabel("Identifier", "Nsname")
             .not(has("token", within("T_CONST", "T_FUNCTION")))
             .not(where(__.in("DEFINITION")))
             .not(where(__.in("ANALYZED").has("analyzer", "Constants/IsExtConstant")))
             .not(where(__.in("NAME").hasLabel("Class", "Defineconstant", "Namespace", "As")))
             .not(where(__.in("EXTENDS", "IMPLEMENTS").hasLabel("Class", "Classanonymous", "Interface")))
             .not(where(__.in().hasLabel("Analysis", "Instanceof", "As", "Staticmethod", "Usetrait", "Usenamespace", "Member", "Constant", "Functioncall", "Methodcallname", "Staticmethodcall", "Staticproperty", "Staticclass", "Staticconstant", "Catch", "Parameter")))
             ') ?: array();
        if (is_array($constantMissed)) {
            file_put_contents("{$this->config->log_dir}/constant.missing.txt", implode(PHP_EOL, array_column($constantMissed->toArray(), 'fullcode')));
            $constantMissed = $constantMissed->toInt();
        } else {
            file_put_contents("{$this->config->log_dir}/constant.missing.txt", 'Nothing found');
            $constantMissed = 0;
        }
        $toDump[] = array('',
                          'constant total',
                          $constantCounts,
                          );
        $toDump[] = array('',
                          'constant missed',
                          $constantMissed,
                          );

        $this->storeToDumpArray('hash', $toDump);
    }

    public function checkRulesets($ruleset, array $analyzers): void {
        $sqliteFile = $this->config->dump;

        $sqlite = new \Sqlite3($sqliteFile);
        $sqlite->busyTimeout(\SQLITE3_BUSY_TIMEOUT);

        $query = 'SELECT analyzer FROM resultsCounts WHERE analyzer IN (' . makeList($analyzers) . ')';
        $ran = array();
        $res = $sqlite->query($query);
        while($row = $res->fetchArray(\SQLITE3_ASSOC)) {
            $ran[] = $row['analyzer'];
        }

        if (empty(array_diff($analyzers, $ran))) {
            $query = "INSERT INTO themas (\"id\", \"thema\") VALUES (null, \"$ruleset\")";
            $sqlite->query($query);
        }
    }

    private function expandRulesets(): void {
        $res = $this->dump->fetchTable('resultsCounts', array('analyzer'));
        $analyzers = $res->toList('analyzer');

        $res = $this->dump->fetchTable('themas', array('thema'));
        $ran = $res->toList('thema');

        $rulesets = $this->rulesets->listAllRulesets();
        $rulesets = array_diff($rulesets, $ran);

        $add = array();
        foreach($rulesets as $ruleset) {
            $analyzerList = $this->rulesets->getRulesetsAnalyzers(array($ruleset));

            $diff = array_diff($analyzerList, $analyzers);
            $diff = array_filter($diff, function (string $x): bool { return (substr($x, 0, 5) !== 'Dump/') && (substr($x, 0, 9) !== 'Complete/');  });
            if (empty($diff)) {
                $add[] = array('', $ruleset);
            }
        }

        if (!empty($add)) {
            $this->dump->storeInTable('themas', $add);
        }
    }

    private function newQuery(string $title): Query {
        return new Query(0, $this->config->project, $title, $this->config->executable);
    }

    public function collect(): void {
        $timer = new Timer();
        $this->collectFiles();

        $this->collectStructures();

        $timer->end();
        $this->log->log( 'Collected Structures: ' . number_format($timer->duration(Timer::MS), 2) . "ms\n");

        // Dev only
        if ($this->config->is_phar === Config::IS_NOT_PHAR) {
            $timer = new Timer();
            $this->collectMissingDefinitions();
            $timer->end();
            $this->log->log( 'Collected Missing definitions : ' . number_format($timer->duration(Timer::MS), 2) . "ms\n");
        }
    }

    private function storeToDump(string $table, Query $query): int {
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            return 0;
        }
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());

        return $this->dump->storeInTable($table, $result);
    }

    private function storeToDumpArray(string $table, array $result): int {
        return $this->dump->storeInTable($table, $result);
    }

    private function loadSqlDump(): void {
        $dumps = glob($this->config->tmp_dir . '/dump-*.php');
        display('Loading ' . count($dumps) . ' dumped SQL files');

        foreach($dumps as $dump) {
            include $dump;

            $this->dump->storeQueries($queries);
            unlink($dump);
        }
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Analyzer\Analyzer;
use Exakat\Tasks\Helpers\Lock;
use Exakat\Helpers\Timer;
use Exakat\Exceptions\NeedsAnalyzerThema;
use Exakat\Exceptions\NoSuchAnalyzer;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\NoSuchRuleset;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Exceptions\QueryException;
use Exakat\Exceptions\MissingGremlin;
use Exakat\Exceptions\DSLException;
use ProgressBar\Manager as ProgressBar;
use Exception;
use Exakat\Log;

class Analyze extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    private $progressBar = null;
    private $php = null;
    private $analyzed = array();

    public function run(): void {
        if (!$this->config->project->validate()) {
            throw new InvalidProjectName($this->config->project->getError());
        }

        if ($this->config->project->isDefault()) {
            throw new ProjectNeeded();
        }

        if ($this->config->gremlin === 'NoGremlin') {
            throw new MissingGremlin();
        }

        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject((string) $this->config->project);
        }

        $this->checkTokenLimit();

        // Take this before we clean it up
        $this->checkAnalyzed();

        if (!empty($this->config->program)) {
            if (is_array($this->config->program)) {
                $analyzersClass = $this->config->program;
            } else {
                $analyzersClass = array($this->config->program);
            }

            foreach($analyzersClass as $analyzer) {
                if (!$this->rulesets->getClass($analyzer)) {
                    throw new NoSuchAnalyzer($analyzer, $this->rulesets);
                }
            }

            if (empty($analyzersClass)) {
                throw new NeedsAnalyzerThema();
            }

        } elseif (!empty($this->config->project_rulesets)) {
            $ruleset = $this->config->project_rulesets;

            if ($ruleset[0] === 'Complete') {
                $all = $this->config->projectConfig ?? $this->config->exakatConfig;

                $ruleset = array();
                foreach($all->project_rulesets as $rule) {

                    if (empty($this->datastore->getHash(trim($rule, '"')))) {
                        $ruleset[] = $rule;
                    }
                }

                // drop the first one, as it may be already running
                array_unshift($ruleset);
                if (empty($ruleset)) {
                    display('All needed ruleset are done. Aborting.');
                    die();
                }

                print 'Completing the following rulesets ' . implode(', ', $ruleset) . PHP_EOL;
            }

            if ((!$analyzersClass = $this->rulesets->getRulesetsAnalyzers($ruleset)) && ($ruleset[0] !== 'None')) {
                throw new NoSuchRuleset(implode(', ', $ruleset), $this->rulesets->getSuggestionRuleset($ruleset));
            }

            // @todo : unidentified rules are omitted at execution time
            // may be we could spot them here, and fix or report

            $this->datastore->addRow('hash', array(implode('-', $this->config->project_rulesets) => count($analyzersClass) ) );

            $this->logname = 'analyze.' . strtolower(str_replace(' ', '_', implode('-', $this->config->project_rulesets)));
            $this->log = new Log('analyze.' . strtolower(str_replace(' ', '_', implode('-', $this->config->project_rulesets))),
                                 "{$this->config->projects_root}/projects/{$this->config->project}");
        } else {
            throw new NeedsAnalyzerThema();
        }

        $this->log->log('Analyzing project ' . (string) $this->config->project);
        $this->log->log("Runnable analyzers\t" . count($analyzersClass));

        $this->php = exakat('php');

        $analyzers = array();
        $dependencies = array();
        foreach($analyzersClass as $analyzerClass) {
            $this->fetchAnalyzers($analyzerClass, $analyzers, $dependencies);
        }

        $analyzerList = sort_dependencies($dependencies);
        if (empty($analyzerList)) {
            display("Done\n");
            return;
        }
        if ($this->config->verbose && !$this->config->quiet) {
            $this->progressBar = new Progressbar(0, count($analyzerList) + 1, $this->config->screen_cols);
        }

        foreach($analyzerList as $analyzerClass) {
            if ($this->config->verbose && !$this->config->quiet) {
                echo $this->progressBar->advance();
            }

            assert($analyzers[$analyzerClass] !== null, "Unknown analyzer $analyzerClass from dependsOn()\n");
            $this->analyze($analyzers[$analyzerClass], $analyzerClass);
        }

        if ($this->config->verbose && !$this->config->quiet) {
            echo $this->progressBar->advance();
        }

        display("Done\n");
    }

    private function fetchAnalyzers(string $analyzerClass, array &$analyzers, array &$dependencies): void {
        if (isset($analyzers[$analyzerClass])) {
            return;
        }

        $analyzers[$analyzerClass] = $this->rulesets->getInstance($analyzerClass);

        if ($analyzers[$analyzerClass] === null) {
            display("No such analyzer as $analyzerClass\n");
            return;
        }

        if (isset($this->analyzed[$analyzerClass]) &&
            $this->config->noRefresh === true) {
            display("$analyzerClass is already processed\n");
            return ;
        }

        if (!empty($this->config->rules_version_max) &&
            version_compare($this->config->rules_version_max,$analyzers[$analyzerClass]->getExakatSince()) < 0) {
            display("$analyzerClass is too new (rules_version_max: {$this->config->rules_version_max})\n");
            return;
        }

        if (!empty($this->config->rules_version_min) &&
            version_compare($this->config->rules_version_min,$analyzers[$analyzerClass]->getExakatSince()) > 0) {
            display("$analyzerClass is too old (rules_version_min: {$this->config->rules_version_min})\n");
            return;
        }

        if ($this->config->noDependencies === true) {
            $dependencies[$analyzerClass] = array();
        } else {
            $dependencies[$analyzerClass] = $analyzers[$analyzerClass]->dependsOn();
            $diff = array_diff($dependencies[$analyzerClass], array_keys($analyzers));
            foreach($diff as $d) {
                if (!isset($analyzers[$d])) {
                    $this->fetchAnalyzers($d, $analyzers, $dependencies);
                }
            }
        }
    }

    private function analyze(Analyzer $analyzer, string $analyzerClass): int {
        $timer = new Timer();

        $lock = new Lock($this->config->tmp_dir, $analyzerClass);
        if (!$lock->check()) {
            display("Concurency lock activated for $analyzerClass\n");

            return 0;
        }

        if (isset($this->analyzed[$analyzerClass]) && $this->config->noRefresh === true) {
            display( "$analyzerClass is already processed (1)\n");
            return $this->analyzed[$analyzerClass];
        }

        $analyzer->init();

        if (!(!isset($this->analyzed[$analyzerClass]) ||
              $this->config->noRefresh !== true)         ) {
            display("$analyzerClass is already processed (2)\n");

            return $this->analyzed[$analyzerClass];
        }

        $total_results = 0;
        if (!$analyzer->checkPhpVersion($this->config->phpversion)) {
            $analyzerQuoted = $analyzer->getInBaseName();

            $analyzer->storeError('Not Compatible With PHP Version', Analyzer::VERSION_INCOMPATIBLE);

            display("$analyzerQuoted is not compatible with PHP version {$this->config->phpversion}. Ignoring\n");
        } elseif (!$analyzer->checkPhpConfiguration($this->php)) {
            $analyzerQuoted = $analyzer->getInBaseName();

            $analyzer->storeError('Not Compatible With PHP Configuration', Analyzer::CONFIGURATION_INCOMPATIBLE);

            display( "$analyzerQuoted is not compatible with PHP configuration of this version. Ignoring\n");
        } else {
            display( "$analyzerClass running\n");
            try {
                $analyzer->run();
            } catch(DSLException $e) {
                $timer->end();
                display( "$analyzerClass : DSL building exception\n");
                display($e->getMessage() . ' in ' . $e->getFile() . ':' . $e->getLine());
                $this->log->log("$analyzerClass\t" . ($timer->duration()) . "\terror : " . $e->getMessage());
                $this->datastore->addRow('analyzed', array($analyzerClass => 0 ) );
                $this->checkAnalyzed();

            } catch(QueryException $e) {
                $timer->end();
                display("$analyzerClass : Query running exception\n");
                display($e->getMessage());
                $this->log->log("$analyzerClass\t" . ($timer->duration()) . "\terror : " . $e->getMessage());
                $counts = $this->gremlin->query('g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer->getInBaseName() . '").property("count", __.out("ANALYZED").count()).values("count")')->toInt();
                $this->datastore->addRow('analyzed', array($analyzerClass => $counts ) );
                $this->checkAnalyzed();

            } catch(Exception $e) {
                $timer->end();
                display( "$analyzerClass : generic exception \n");
                $this->log->log("$analyzerClass\t" . ($timer->duration()) . "\texception : " . get_class($e) . "\terror : " . $e->getMessage());
                if (strpos($e->getMessage(), 'The server exceeded one of the timeout settings ') === false) {
                    display($e->getMessage());
                    $this->datastore->addRow('analyzed', array($analyzerClass => 0 ) );
                } else {
                    $counts = $this->gremlin->query('g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer->getInBaseName() . '").property("count", __.out("ANALYZED").count()).values("count")')->toInt();
                    $this->datastore->addRow('analyzed', array($analyzerClass => $counts ) );
                }
                $this->checkAnalyzed();

                return 0;
            }

            $total_results = $analyzer->getRowCount();
            $processed     = $analyzer->getProcessedCount();
            $queries       = $analyzer->getQueryCount();
            $rawQueries    = $analyzer->getRawQueryCount();

            display( "$analyzerClass run ($total_results / $processed)\n");
            $timer->end();
            $this->log->log("$analyzerClass\t" . ($timer->duration()) . "\t$total_results\t$processed\t$queries\t$rawQueries");
            // storing the number of row found in Hash table (datastore)
            $this->datastore->addRow('analyzed', array($analyzerClass => $total_results ) );

            // This also counts the analysis that don't leave data in the database.
            $this->analyzed[$analyzerClass] = $total_results;
        }

        $this->checkAnalyzed();

        return $total_results;
    }

    private function checkAnalyzed(): void {
        $query = <<<'GREMLIN'
g.V().hasLabel("Analysis").as("analyzer", "count").select("analyzer", "count").by("analyzer").by("count");
GREMLIN;
        $res = $this->gremlin->query($query);

        foreach($res as list('analyzer' => $analyzer, 'count' => $count)) {
            if ($count != -1) {
                $this->analyzed[$analyzer] = $count;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\NoSuchAnalyzer;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\ProjectNotInited;
use Exakat\Exceptions\NoDump;
use Exakat\Exceptions\NeedsAnalyzerThema;

class Results extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        if ($this->config->project->isDefault()) {
            throw new ProjectNeeded();
        }

        if (!$this->config->project->validate()) {
            throw new InvalidProjectName($this->config->project->getError());
        }

        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($this->config->project);
        }

        if (!file_exists($this->config->datastore)) {
            throw new ProjectNotInited($this->config->project);
        }

        if (!file_exists($this->config->dump)) {
            throw new NoDump((string) $this->config->project);
        }

        if (!empty($this->config->program)) {
            if (is_array($this->config->program)) {
                $analyzersClass = $this->config->program;
            } else {
                $analyzersClass = array($this->config->program);
            }

            foreach($analyzersClass as $analyzer) {
                if (!$this->rulesets->getClass($analyzer)) {
                    throw new NoSuchAnalyzer($analyzer, $this->rulesets);
                }
            }
        } elseif (!empty($this->config->project_rulesets)) {
            $project_rulesets = $this->config->project_rulesets;

            if (!$analyzersClass = $this->rulesets->getRulesetsAnalyzers($project_rulesets)) {
                throw new NoSuchAnalyzer($project_rulesets, $this->rulesets);
            }
        } else {
            throw new NeedsAnalyzerThema();
        }

        $return = array();
        if ($this->config->style === 'BOOLEAN') {
            $queryTemplate = <<<GREMLIN
g.V().hasLabel("Analysis").has("analyzer", "$analyzer").out().count().is(gt(0))
GREMLIN;
            $vertices = $this->gremlin->query($queryTemplate);

            $return[] = $vertices[0];
        } elseif ($this->config->style === 'COUNTED_ALL') {
            $queryTemplate = <<<GREMLIN
g.V().hasLabel("Analysis").has("analyzer", "$analyzer").out().count()
GREMLIN;
            $vertices = $this->gremlin->query($queryTemplate)->results;

            $return[] = $vertices[0];
        } elseif ($this->config->style === 'ALL') {
            $results = array();

            foreach($analyzersClass as $oneAnalyzerClass) {
                $analyzer =  $this->rulesets->getInstance($oneAnalyzerClass, null, $this->config);
                $results[] = $analyzer->getDump();
            }

            $return = array_merge(...$results);
        } elseif ($this->config->style === 'DISTINCT') {
            $queryTemplate = 'g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer . '").out("ANALYZED").values("code").unique()';
            $vertices = $this->gremlin->query($queryTemplate)->results;

            $return = array();
            foreach($vertices as $values) {
                $return[] = array($values);
            }
        } elseif ($this->config->style === 'COUNTED') {
            $queryTemplate = 'g.V().hasLabel("Analysis").has("analyzer", "' . $analyzer . '").out("ANALYZED").groupCount("m").by("code").cap("m")';
            $vertices = $this->gremlin->query($queryTemplate)->results;

            $return = array();
            foreach($vertices[0] as $k => $values) {
                $return[$k] = $values;
            }
        }

        if ($this->config->json === true) {
            $text = json_encode($return);
        } elseif ($this->config->csv === true) {
            $text = array(array('Code', 'File', 'Namespace', 'Class', 'Function'));
            foreach($return as $k => $v) {
                if (is_array($v)) {
                    $text[] = $v;
                } else {
                    $text[] = array($k, $v);
                }
            }
        } elseif ($this->config->html === true || $this->config->odt === true) {
            $text = '';
            foreach($return as $k => $r) {
                if ($this->config->style === 'COUNTED') {
                    $text .= "+ $k => $r\n";
                } else {
                    $text .= "+ $k\n";
                    if (is_array($r)) {
                        $text .= '  + ' . implode("\n  + ", $r) . "\n";
                    } else {
                        $text .= "+ $r\n";
                    }
                }
            }
        } else {
            // count also for $this->config->text == 1
            $text = '';
            foreach($return as $k => $v) {
                if ($this->config->style === 'COUNTED') {
                    $text .= "$k => $v\n";
                } else {
                    $text .= implode(', ', $v) . "\n";
                }
            }
        }

        if ($this->config->output) {
            echo $text;
        }

        switch (1) {
            case $this->config->json :
                $extension = 'json';
                break 1;
            case $this->config->odt :
                $extension = 'odt';
                break 1;
            case $this->config->html :
                $extension = 'html';
                break 1;
            case $this->config->csv :
                $extension = 'csv';
                break 1;
            case $this->config->text :
            default :
                $extension = 'txt';
                break 1;
        }

        if ($this->config->file != '') {
            $name = $this->config->file . '.' . $extension;
            if (file_exists($name)) {
                die( "$name already exists. Aborting\n");
            }

            if ($this->config->format === 'CSV') {
                $csvFile = fopen($name, 'w');
                if (is_resource($csvFile)) {
                    foreach($text as $t) {
                        fputcsv($csvFile, $t);
                    }
                    fclose($csvFile);
                } else {
                    die( "Couldn't open $name file for writing. Aborting\n");
                }
            } else {
                file_put_contents($name, $text);
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\ProjectNeeded;

class Show extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        $project = $this->config->project;

        if (!$project->validate()) {
            throw new InvalidProjectName($project->getError());
        }

        if ($this->config->project === 'default') {
            throw new ProjectNeeded();
        }

        print "Create project '$project' with : 
    {$this->config->php} {$this->config->executable} init -p $project -R {$this->config->project_url} -{$this->config->project_vcs} -v\n";
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\NoSuchProject;
use Exakat\Tasks\Helpers\BaselineStash;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Exceptions\InvalidProjectName;

class Baseline extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    private const FORMAT = "+ %-4s %10s %12s\n";

    public const ACTIONS = array('list', 'remove', 'save');

    //install, list, local, uninstall, upgrade
    public function run(): void {
        if (!$this->config->project->validate()) {
            throw new InvalidProjectName($this->config->project->getError());
        }

        if ($this->config->project->isDefault()) {
            throw new ProjectNeeded();
        }

        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject((string) $this->config->project);
        }

        if (in_array($this->config->subcommand, self::ACTIONS)) {
            $this->{$this->config->subcommand}();
        } else {
            $this->list();
        }
    }

    private function list() {
        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($this->config->project);
        }

        $list = glob($this->config->project_dir . '/baseline/*.sqlite');
        sort($list);

        print PHP_EOL;
        printf(self::FORMAT, '#', 'Name', 'Date');
        print str_repeat('-', 40) . PHP_EOL;
        foreach($list as $l) {
            if (preg_match('/^dump-(\d+)-(.*?)$/', basename($l, '.sqlite'), $r) ) {
                list(, $id, $name) = $r;
            } else {
                $id = ' ';
                $name = basename($l, '.sqlite');
            }
            $date = date('Y-m-d', filemtime($l));
            printf(self::FORMAT, $id, $name, $date);
        }

        print PHP_EOL . 'Total : ' . count($list) . ' baseline' . (count($list) > 1 ? 's' : '') . PHP_EOL;
    }

    private function remove() {
        $baselineStash = new BaselineStash($this->config);
        $baselineStash->removeBaseline($this->config->baseline_id);
    }

    private function save() {
        $baselineStash = new BaselineStash($this->config);
        $baselineStash->copyPrevious($this->config->dump, $this->config->baseline_set);
        display('Save current audit to ' . $this->config->baseline_set);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Phpexec;
use Exakat\Config;
use Exakat\Exceptions\NoCodeInProject;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Fileset\{All, Filenames, FileExtensions, IgnoreDirs};

class Files extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    private $tmpFileName = '';

    public function run(): void {
        $stats = array();
        foreach(Config::PHP_VERSIONS as $version) {
            $stats["notCompilable$version"] = 'N/C';
        }

        if ($this->config->inside_code === Config::INSIDE_CODE) {
            // OK
        } elseif (!empty($this->config->filename)) {
            // OK
        } elseif ($this->config->project === 'default') {
            throw new ProjectNeeded();
        } elseif (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($this->config->project);
        } elseif (!file_exists($this->config->code_dir)) {
            throw new NoCodeInProject($this->config->project);
        }

        $this->checkComposer($this->config->code_dir);
        $this->checkLicence($this->config->code_dir);

        $ignoredFiles = array();
        $files = array();

        display("Searching for files \n");
        $set = new All($this->config->code_dir);
        $set->addFilter(new Filenames($this->config->dir_root));
        $set->addFilter(new FileExtensions($this->config->file_extensions));
        $set->addFilter(new IgnoreDirs($this->config->ignore_dirs, $this->config->include_dirs));

        $files = $set->getFiles();
        $ignoredFiles = $set->getIgnored();
        display('Found ' . count($files) . " files.\n");

        $filesRows = array();
        $hashes = array();
        $duplicates = 0;
        foreach($files as $id => $file) {
            $fnv132 = hash_file('fnv132', $this->config->code_dir . $file);
            if (isset($hashes[$fnv132])) {
                $ignoredFiles[$file] = "Duplicate ({$hashes[$fnv132]})";
                ++$duplicates;
                unset($files[$id]);
                continue;
            } else {
                $hashes[$fnv132] = $file;
            }
            $modifications = 0;
            $filesRows[] = compact('file', 'fnv132', 'modifications');
        }
        display("Removed $duplicates duplicates files\n");

        if (empty($files)) {
            $this->datastore->addRow('hash', array('files'           => 0,
                                                   'filesIgnored'    => count($ignoredFiles),
                                                   'tokens'          => 0,
                                                   'file_extensions' => json_encode($this->config->file_extensions),
                                                   'ignore_dirs'     => json_encode($this->config->ignore_dirs),
                                                   'include_dirs'    => json_encode($this->config->include_dirs),
                                               )
                                            );
            return;
        }

        $this->tmpFileName = "{$this->config->tmp_dir}/files{$this->config->pid}.txt";
        $tmpFiles = array_map(function (string $file): string {
            return str_replace(array('\\', '(', ')', ' ', '$', '<', "'", '"', ';', '&', '`', '|', "\t"),
                               array('\\\\', '\\(', '\\)', '\\ ', '\\$', '\\<', "\\'", '\\"', '\\;', '\\&', '\\`', '\\|', "\\\t", ),
                               ".$file");
                               }, $files);
        file_put_contents($this->tmpFileName, implode("\n", $tmpFiles));

        $SQLresults = $this->checkCompilations();

        $SQLresults += $this->checkShortTags();

        $i = array();
        foreach($ignoredFiles as $file => $reason) {
            $i[] = compact('file', 'reason');
        }
        $ignoredFiles = $i;
        $this->datastore->cleanTable('ignoredFiles');
        $this->datastore->addRow('ignoredFiles', $ignoredFiles);

        $this->datastore->cleanTable('files');

        $this->datastore->addRow('files', $filesRows);
        $this->datastore->addRow('hash', array('files'           => count($files),
                                               'filesIgnored'    => count($ignoredFiles),
                                               'tokens'          => 0,
                                               'file_extensions' => json_encode($this->config->file_extensions),
                                               'ignore_dirs'     => json_encode($this->config->ignore_dirs),
                                               'include_dirs'    => json_encode($this->config->include_dirs),
                                               )
                                            );
        $this->datastore->reload();

        $stats['php'] = count($files);
        $this->datastore->addRow('hash', $stats);

        // check for special files
        display('Check config files');
        // Avoid , GLOB_BRACE
        $files = array_merge(glob("{$this->config->code_dir}/.*"),
                             glob("{$this->config->code_dir}/*")) ;
        $files = array_map('basename', $files);

        $services = json_decode(file_get_contents("{$this->config->dir_root}/data/serviceConfig.json"));

        $configFiles = array();
        foreach($services as $name => $service) {
            $diff = array_intersect((array) $service->file, $files);
            foreach($diff as $d) {
                $configFiles[] = array('file'     => $d,
                                       'name'     => $name,
                                       'homepage' => $service->homepage);
            }
        }
        $this->datastore->addRow('configFiles', $configFiles);
        // Composer is checked previously

        $files = array();
        $i = 0;
        while(count($files) != $SQLresults) {
            $files = glob("{$this->config->project_dir}/.exakat/dump-*.php");
            usleep(random_int(0,1000) * 1000);

            ++$i;
            if ($i >= 60) {
                break 1;
            }
        }
        // TODO : log it when

        foreach($files as $file) {
            include $file;

            $this->datastore->storeQueries($queries);
            unlink($file);
        }

        display('Done');

        if ($this->config->json) {
            echo json_encode($stats);
        }
        $this->datastore->addRow('hash', array('status' => 'Initproject'));
        $this->checkTokenLimit();
    }

    private function checkComposer(string $dir): void {
        // composer.json
        display('Check composer');
        $composerInfo = array();
        if ($composerInfo['composer.json'] = file_exists("{$dir}/composer.json")) {
            $composerInfo['composer.lock'] = file_exists("{$dir}/composer.lock");

            $composer = json_decode(file_get_contents("{$dir}/composer.json"));

            if (isset($composer->autoload)) {
                $composerInfo['autoload'] = isset($composer->autoload->{'psr-0'}) ? 'psr-0' : 'psr-4';
            } else {
                $composerInfo['autoload'] = false;
            }

            if (isset($composer->require)) {
                $this->datastore->addRow('composer', (array) $composer->require);
            }
        }

        $this->datastore->addRow('hash', $composerInfo);
    }

    private function checkLicence(string $dir): bool {
        $licenses = parse_ini_file($this->config->dir_root . '/data/license.ini');
        $licenses = $licenses['files'];

        foreach($licenses as $file) {
            if (file_exists("$dir/$file")) {
                $this->datastore->addRow('hash', array('licence_file' => 'unknown'));

                return true;
            }
        }
        $this->datastore->addRow('hash', array('licence_file' => 'unknown'));

        return false;
    }

    public function __destruct() {
        if (file_exists($this->tmpFileName)) {
            unlink($this->tmpFileName);
        }
        if (file_exists($this->config->tmp_dir . '/lint.php')) {
            unlink($this->config->tmp_dir . '/lint.php');
        }
        if (file_exists($this->config->tmp_dir . '/lint_short_tags.php')) {
            unlink($this->config->tmp_dir . '/lint_short_tags.php');
        }
    }

    private function checkCompilations(): int {
        $versions = Config::PHP_VERSIONS;
        $SQLresults = 0;

        $analyzingVersion = $this->config->phpversion[0] . $this->config->phpversion[2];
        $this->datastore->cleanTable("compilation$analyzingVersion");
        if ($this->is_subtask === self::IS_SUBTASK) {
            $id = array_search($analyzingVersion, $versions);
            unset($versions[$id]);
        }

        foreach($versions as $version) {
            $phpVersion = "php$version";

            if (empty($this->config->{$phpVersion})) {
                // This version is not defined
                continue;
            }

            display("Check compilation for $version");

            try {
                $php = new Phpexec($phpVersion, $this->config->{$phpVersion});
            } catch (\Exception $e) {
                // Skip compilation check if PHP is not available
                continue;
            }
            $php->compileFiles($this->config->code_dir, $this->tmpFileName, $this->config->dir_root);
            ++$SQLresults;
        }

        return $SQLresults;
    }

    private function checkShortTags(): int {
        copy("{$this->config->dir_root}/server/lint_short_tags.php", "{$this->config->project_dir}/.exakat/lint_short_tags.php");
        $shell = "nohup php {$this->config->project_dir}/.exakat/lint_short_tags.php {$this->config->php} {$this->config->project_dir} {$this->tmpFileName} 2>&1 >/dev/null & echo $!";
        shell_exec($shell);

        return 1;
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\NoSuchDir;
use Exakat\Exceptions\NoSuchFile;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\NoCodeInProject;

class Anonymize extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    private $lnumberValues = array();
    private $lnumber = 0;
    private $dnumberValues = array();
    private $dnumber = 0;
    private $variableNames = array();
    private $variables = 'a';
    private $stringsNames = array();
    private $strings = 'A';

    public function run(): void {

        if (($file = $this->config->file) === 'stdout') {
            $dir = $this->config->dirname;
            if (!empty($dir)) {
                $dir = rtrim($dir, '/');

                if (!file_exists($dir)) {
                    throw new NoSuchDir($file);
                }

                display("Anonymizing directory $dir\n");

                $files = rglob($dir);
                $total = 0;
                if (file_exists($dir . '.anon')) {
                    rmdirRecursive($dir . '.anon');
                }
                mkdir($dir . '.anon', 0755);
                foreach($files as $file) {
                    if ($this->checkCompilation($file)) {
                        ++$this->strings;
                        $total += (int) $this->processFile($file, $dir . '.anon/' . $this->strings . '.php');
                    }
                }
                display("Anonymized $total files\n");
            } elseif (($project = $this->config->project) !== 'default') {
                display("Anonymizing project $project\n");
                $dir = $this->config->projects_root . '/projects/' . $project . '/' . $project;

                if (!file_exists($this->config->projects_root . '/projects/' . $project)) {
                    throw new NoSuchProject($project);
                }

                if (!file_exists($this->config->projects_root . '/projects/' . $project . '/code')) {
                    throw new NoCodeInProject($project);
                }

                $files = $this->datastore->getCol('files', 'file');

                $path = $this->config->projects_root . '/projects/' . $this->config->project . '/code';

                $total = 0;
                if (file_exists($dir . '.anon')) {
                    rmdirRecursive($dir . '.anon');
                }
                mkdir($dir . '.anon', 0755);
                foreach($files as $file) {
                    if ($this->checkCompilation($path . $file)) {
                        ++$this->strings;
                        $total += (int) $this->processFile($path . $file, $dir . '.anon/' . $this->strings . '.php');
                    }
                }
                display("Anonymized $total files\n");
            } else {
                die("Usage : php exakat anonymize -file <filename>
                                 -d <dirname>
                                 -p <project>\n");
            }
        } else {
            display("Anonymizing file $file\n");

            if (!file_exists($file)) {
                throw new NoSuchFile($file);
            }

            if (!$this->checkCompilation($file)) {
                die('Can\'t anonymize ' . $file . ' as it doesn\'t compile with PHP ' . PHP_VERSION . "\n");
            }
            $this->processFile($file);
        }

        display( 'Processing file ' . $file . ' into ' . $file . ".anon\n");
    }

    private function processFile(string $file, string $anonFile = null): bool {
        $php = file_get_contents($file);
        $tokens = token_get_all($php);
        if (count($tokens) < 3) {
            display( "Ignoring $file, no PHP-code.\n");
            return false;
        }

        $checks = array('T_TRAIT',
                        'T_FINALLY',
                        'T_YIELD',
                        'T_YIELD_FROM',
                        'T_COALESCE',
                        'T_CHARACTER',
                        'T_BAD_CHARACTER',
                        'T_SPACESHIP',
                        );
        foreach($checks as $check) {
            if (!defined($check)) {
                define($check, 1);
            }
        }

        $php = '';
        foreach($tokens as $t) {
            if (!is_array($t)) {
                $php .= $t;

                continue;
            }

            switch($t[0]) {
                case T_LNUMBER:  // integers
                    if (isset($this->lnumberValues[$t[1]])) {
                        $t[1] = $this->lnumberValues[$t[1]];
                    } else {
                        $this->lnumberValues[$t[1]] = ++$this->lnumber;
                        $t[1] = $this->lnumberValues[$t[1]];
                    }
                    break;
                case T_DNUMBER:  // real numbers
                    if (isset($this->dnumberValues[$t[1]])) {
                        $t[1] = floor(random_int(0, PHP_INT_MAX) ) / 100;
                    } else {
                        $this->dnumberValues[$t[1]] = ++$this->dnumber;
                        $t[1] = floor(random_int(0, PHP_INT_MAX) ) / 100;
                    }
                    break;
                case T_VARIABLE:
                    if ($t[1] != '$this') {
                        if (isset($this->variableNames[$t[1]])) {
                            $t[1] = $this->variableNames[$t[1]];
                        } else {
                            $this->variableNames[$t[1]] = '$' . ++$this->variables;
                            $t[1] = $this->variableNames[$t[1]];
                        }
                    }
                    break;
                case T_CONSTANT_ENCAPSED_STRING:
                    ++$this->strings;
                    if (in_array($this->strings, array('IF', 'AS', 'DO', 'OR'))) {
                        ++$this->strings;
                    }
                    if (isset($this->stringsNames[$t[1]])) {
                        $t[1] = $this->stringsNames[$t[1]];
                    } else {
                        $this->stringsNames[$t[1]] = "'" . $this->strings . "'";
                        $t[1] = $this->stringsNames[$t[1]];
                    }
                    break;
                case T_STRING:
                case T_NUM_STRING:
                    if (strtolower($t[1]) == 'null') { break ; }
                    // otherwise, fall through!
                case T_ENCAPSED_AND_WHITESPACE :
                    ++$this->strings;
                    if (in_array($this->strings, array('IF', 'AS', 'DO', 'OR'))) {
                        ++$this->strings;
                    }
                    if (isset($this->stringsNames[$t[1]])) {
                        $t[1] = $this->stringsNames[$t[1]];
                    } else {
                        $this->stringsNames[$t[1]] = $this->strings;
                        $t[1] = $this->stringsNames[$t[1]];
                    }
                    break;
                case T_DOC_COMMENT:
                case T_COMMENT:
                    $t[1] = "\n";
                    break;

                case T_INLINE_HTML :
                    ++$this->strings;
                    if (isset($this->stringsNames[$t[1]])) {
                        $t[1] = $this->stringsNames[$t[1]];
                    } else {
                        $this->stringsNames[$t[1]] = $this->strings;
                        $t[1] = $this->stringsNames[$t[1]];
                    }
                    break;

                case T_START_HEREDOC:
                    ++$this->strings;
                    $short = substr($t[1], 3);

                    if (!isset($this->stringsNames[$short])) {
                        $this->stringsNames[$short] = $this->strings;
                    }

                    if ($short[0] == "'") {
                        $t[1] = "<<<'" . $this->stringsNames[$short] . "'\n";
                    } else {
                        $t[1] = '<<<' . $this->stringsNames[$short] . "\n";
                    }

                    $heredoc = "\n" . $this->stringsNames[$short];

                    break;

                case T_END_HEREDOC:
                    $t[1] = $heredoc;
                    unset($heredoc);

                    break;

                case T_ISSET :
                case T_EXIT :

                case T_ARRAY_CAST :
                case T_BOOL_CAST :
                case T_DOUBLE_CAST :
                case T_OBJECT_CAST :
                case T_UNSET_CAST :
                case T_INT_CAST :
                case T_STRING_CAST :

                case T_CONST :
                case T_LIST :

                case T_NAMESPACE :
                case T_IMPLEMENTS :

                case T_MUL_EQUAL :
                case T_DIV_EQUAL :

                case T_RETURN :
                case T_SWITCH :
                case T_CASE :
                case T_DEFAULT :
                case T_ENDSWITCH :
                case T_ECHO :
                case T_PRINT :
                case T_EMPTY :
                case T_ARRAY :
                case T_GLOBAL :
                case T_TRY :
                case T_CATCH :
                case T_DOUBLE_ARROW :
                case T_CURLY_OPEN:
                case T_ELSE :
                case T_PUBLIC :
                case T_PROTECTED :
                case T_PRIVATE :
                case T_FINAL :

                case T_SL :
                case T_SR :
                case T_IS_EQUAL :
                case T_IS_SMALLER_OR_EQUAL :
                case T_MINUS_EQUAL :
                case T_WHILE :
                case T_ENDWHILE :
                case T_IS_GREATER_OR_EQUAL :
                case T_PLUS_EQUAL :
                case T_POW :
                case T_CLASS :
                case T_INTERFACE :
                case T_CONTINUE :
                case T_WHITESPACE :
                case T_AS :
                case T_BOOLEAN_OR :
                case T_BOOLEAN_AND :
                case T_BREAK :
                case T_DEC :
                case T_DO :
                case T_IS_NOT_IDENTICAL :
                case T_SR_EQUAL :
                case T_XOR_EQUAL :
                case T_OR_EQUAL :
                case T_IS_NOT_EQUAL :
                case T_COALESCE :

                case T_OPEN_TAG_WITH_ECHO :
                case T_CALLABLE :
                case T_UNSET :
                case T_EVAL :

                case T_DOLLAR_OPEN_CURLY_BRACES :

                case T_FINALLY :
                case T_YIELD :
                case T_YIELD_FROM :

                case T_FOR :
                case T_ENDFOR :
                case T_FOREACH :
                case T_ENDFOREACH :

                case T_FUNCTION :
                case T_INC:
                case T_DOUBLE_COLON:
                case T_THROW:
                case T_NEW:
                case T_CLONE:
                case T_ELLIPSIS:
                case T_NS_SEPARATOR:
                case T_OBJECT_OPERATOR:
                case T_DIR:
                case T_STATIC:
                case T_VAR :
                case T_OPEN_TAG:
                case T_CLOSE_TAG:
                case T_INSTANCEOF:

                case T_INCLUDE :
                case T_INCLUDE_ONCE :
                case T_REQUIRE :
                case T_REQUIRE_ONCE :

                case T_TRAIT :
                case T_EXTENDS :
                case T_USE :
                case T_INSTEADOF :

                case T_LOGICAL_AND :
                case T_LOGICAL_OR :
                case T_LOGICAL_XOR :

                case T_IF:
                case T_ELSEIF:
                case T_ENDIF :

                case T_FILE :
                case T_CLASS_C :
                case T_FUNC_C :
                case T_LINE :
                case T_METHOD_C :
                case T_NS_C :

                case T_POW_EQUAL :
                case T_SL_EQUAL :
                case T_AND_EQUAL :
                case T_MOD_EQUAL :

                case T_DECLARE :
                case T_ENDDECLARE :
                case T_GOTO :
                case T_ABSTRACT :
                case T_HALT_COMPILER :
                case T_TRAIT_C :
                case T_PAAMAYIM_NEKUDOTAYIM :

                case T_STRING_VARNAME : //complex variable parsed syntax
                case T_CHARACTER :      // Not used
                case T_BAD_CHARACTER :  //anything below ASCII 32 except \t (0x09), \n (0x0a) and \r (0x0d)

                case T_IS_IDENTICAL:
                case T_CONCAT_EQUAL:
                    // simply ignore
                    break;

                default:
                    echo token_name($t[0]), " is unknown token (Token : $t[0] '$t[1]', line $t[2])\n";
            }

                $php .= $t[1];
            }

        if ($anonFile === null) {
            $anonFile = $file . '.anon';
        }

        file_put_contents($anonFile, $php);

        return true;
    }

    private function checkCompilation(string $file): bool {
        $res = shell_exec($this->config->php . ' -l ' . $file . ' 2>&1') ?? '';
        //@todo : differentiate fatal error and non-fatal ones.
        return substr($res, 0, 28) == 'No syntax errors detected in';
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\VcsSupport;
use Exakat\Vcs\Vcs;
use Exakat\Export\Export as ExportFormat;

class Export extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public const OPEN_TAG  = '<?php ' . PHP_EOL;
    public const CLOSE_TAG = PHP_EOL . '?' . '>';

    public const NO_NEXT = -1;

    private $php             = null;

    public function run(): void {
        $gremlinVersion = $this->gremlin->serverInfo()[0];
        $this->php = exakat('php');

        if (version_compare($gremlinVersion, '3.4.0') >= 0) {
            $queryTemplate = 'g.V().valueMap().with(WithOptions.tokens).by(unfold())';
        } else {
            $queryTemplate = 'g.V()';
        }

        $vertices = $this->gremlin->query($queryTemplate, array());

        $V = array();
        $root = 0;
        foreach($vertices as $v) {
            if ($v['label'] === 'Project') {
                $root = $v['id'];
            }
            $V[$v['id']] =  $v;
        }

        if (version_compare($gremlinVersion, '3.4.0') >= 0) {
            $queryTemplate = 'g.E().as("e").outV().as("outV").select("e").inV().as("inV").select("e", "inV", "outV").by(valueMap(true).by(unfold())).by(id()).by(id())';
        } else {
            $queryTemplate = 'g.E()';
        }
        $edges = $this->gremlin->query($queryTemplate);

        $E = array();
        foreach($edges as $e) {
            // Special for version 3.4
            if (isset($e['e'])) {
                $e = array_merge($e, $e['e']);
            }
            $id = $e['outV'];

            if (!isset($E[$id])) {
                $E[$id] = array();
            }

            array_collect_by($E[$id], $e['inV'], $e['label']);
        }

        if (in_array('Vis', $this->config->project_reports)) {
            $renderer = ExportFormat::getInstance('Vis', $V, $E);
            $text = $renderer->render($root);
            $extension = $renderer->getExtension();
        } elseif (in_array('Dot', $this->config->project_reports)) {
            $renderer = ExportFormat::getInstance('Dot', $V, $E);
            $text = $renderer->render($root);
            $extension = $renderer->getExtension();
        } elseif (in_array('Php', $this->config->project_reports)) {
            $renderer = ExportFormat::getInstance('Php', $V, $E);
            $text = $renderer->render($root);
            $files = json_decode($text, true);
            $extension = $renderer->getExtension();

            if ($this->config->json) {
                echo $text;

                return;
            }

            if (!empty($this->config->filename)) {
                // only one file, the first one
                file_put_contents($this->config->filename[0], array_pop($files));

                $this->checkCompilation($this->config->filename[0]);

                return;
            }

            if (!empty($this->config->dirname)) {
                foreach($files as $file => $code) {
                    if (!file_exists($this->config->dirname . '/' . dirname($file))) {
                        mkdir($this->config->dirname . '/' . dirname($file), 0755);
                    }
                    file_put_contents($this->config->dirname . '/' . $file, $code);
                    $this->checkCompilation($this->config->dirname . '/' . $file);
                }

                return;
            }

            if (!empty($this->config->inplace)) {
                display("Writing in place\n");
                foreach($files as $file => $code) {
                    file_put_contents($this->config->code_dir . '/' . $file, $code);
                    $this->checkCompilation($this->config->code_dir . '/' . $file);
                }

                return;
            }

            if (!empty($this->config->branch)) {
                try {
                    display("Writing in branch : {$this->config->branch}\n");

                    $vcs = Vcs::getVcs($this->config);
                    $vcs = new $vcs($this->config->project, $this->config->code_dir);
                    $branch = $vcs->getBranch();
                    $vcs->createBranch($this->config->branch);

                    foreach($files as $file => $code) {
                        file_put_contents($this->config->code_dir . '/' . $file, $code);
                        $this->checkCompilation($this->config->code_dir . '/' . $file);
                    }

                    $vcs->commitFiles('Exakat cobbler for ' . implode(', ', $this->config->program));
                    $vcs->checkoutBranch($branch);
                } catch (VcsSupport $e) {
                    print "Error while storing in branch {$this->config->branch} : " . $e->getMessage() . "\n";
                }

                return;
            }

            $text = implode('', $files);
        } elseif (in_array('Table', $this->config->project_reports)) {
            $renderer = ExportFormat::getInstance('Table', $V, $E);
            $text = $renderer->render($root);
            $extension = $renderer->getExtension();
        } else {
            $renderer = ExportFormat::getInstance('Text', $V, $E);
            $text = $renderer->render($root);
            $extension = $renderer->getExtension();
        }

        if ($filenames = $this->config->filename) {
            foreach($filenames as $filename) {
                $filename = array_pop($filenames);
                if (in_array('Dot', $this->config->project_reports)) {
                    $fp = fopen($filename . '.dot', 'w+');
                } elseif (in_array('Vis', $this->config->project_reports)) {
                    $fp = fopen($filename . '.html', 'w+');
                } elseif (in_array('Php', $this->config->project_reports)) {
                    $fp = fopen($filename . '.php', 'w+');
                } else {
                    // todo : case this is a folder
                    $fp = fopen($filename, 'w+');
                }
                fwrite($fp, $text);
                fclose($fp);
            }
        } else {
            echo $text;
        }
    }

    private function checkCompilation(string $file): bool {
        if (!($compile = $this->php->compile($file))) {
            display("Warning : '{$file}' doesn't compile. Proceed with caution.");
        }

        return $compile;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exakat;
use Exakat\Config;
use Exakat\Phpexec;
use Exakat\Graph\Graph;
use Exakat\Exceptions\NoPhpBinary;
use Exakat\Exceptions\HelperException;
use Exakat\Exceptions\NoSuchReport;
use Exakat\Tasks\Helpers\ReportConfig;
use Symfony\Component\Yaml\Yaml as Symfony_Yaml;

class Doctor extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    protected $logname = self::LOG_NONE;

    private $reportList = array();

    public function __construct() {
        $this->config  = exakat('config');
        $this->gremlin = exakat('graphdb');
        // Ignoring everything else
    }

    public function run(): void {
        $stats = array_merge($this->checkPreRequisite(),
                             $this->checkAutoInstall());

        $phpBinaries = array('php' . str_replace('.', '', substr(PHP_VERSION, 0, 3)) => PHP_BINARY);
        foreach(Config::PHP_VERSIONS as $shortVersion) {
            $configName = "php$shortVersion";
            if (!empty($this->config->$configName)) {
                $phpBinaries[$configName] = $this->config->$configName;
            }
        }

        $stats = array_merge($stats,
                             $this->checkPHPs($phpBinaries));

        if ($this->config->verbose === true) {
            $stats = array_merge($stats, $this->checkOptional());
        }

        if ($this->config->json === true) {
            print json_encode($stats, JSON_PRETTY_PRINT);
            return;
        }

        if ($this->config->yaml === true) {
            print Symfony_Yaml::dump($stats);
            return;
        }

        if ($this->config->quiet !== true) {
            print $this->displayCli($stats);
        }
    }

    private function displayCli(array $stats): string {
        $stats['exakat']['rulesets']       = $this->array2list($stats['exakat']['rulesets']);
        $stats['exakat']['extra rulesets'] = $this->array2list($stats['exakat']['extra rulesets']);
        $stats['exakat']['ignored rules']  = $this->array2list($stats['exakat']['ignored rules']);
        $stats['exakat']['exakat.ini']     = $this->array2list($stats['exakat']['exakat.ini']);
        $stats['exakat']['reports']        = $this->array2list($stats['exakat']['reports'] );

        if (isset($stats['exakat']['extensions'])) {
            $stats['exakat']['extensions']  = $this->array2list($stats['exakat']['extensions']);
        }

        $doctor = '';
        foreach($stats as $section => $details) {
            $doctor .= $section . ' : ' . PHP_EOL;
            foreach($details as $k => $v) {
                $doctor .= '    ' . substr("$k                          ", 0, 20) . ' : ' . $v . PHP_EOL;
            }
            $doctor .= PHP_EOL;
        }

        return $doctor;
    }

    private function checkPreRequisite(): array {
        $stats = array();

        // Compulsory
        $stats['exakat']['executable']  = $this->config->executable;
        $stats['exakat']['version']     = Exakat::VERSION;
        $stats['exakat']['build']       = Exakat::BUILD;
        $stats['exakat']['exakat.ini']  = $this->config->configFiles;
        $stats['exakat']['graphdb']     = $this->config->graphdb;
        $reportList = array();
        foreach($this->config->project_reports as $project_report) {
            try {
                $reportConfig = new ReportConfig($project_report, $this->config);
            } catch (NoSuchReport $e) {
                display($e->getMessage());
                continue;
            }
            $this->reportList[] = $reportConfig->getName();
        }
        sort($this->reportList);
        $stats['exakat']['reports']        = $reportList;

        $stats['exakat']['rulesets']       = $this->config->project_rulesets;
        $stats['exakat']['extra rulesets'] = array_keys($this->config->rulesets);
        $stats['exakat']['ignored rules']  = $this->config->ignore_rules;

        $stats['exakat']['tokenslimit'] = number_format((int) $this->config->token_limit, 0, '', ' ');
        if ($list = $this->config->ext->getPharList()) {
            $stats['exakat']['extensions']  = $list;
        }

        $stubs = exakat('stubs');
        $files = $stubs->getFile();
        $stats['exakat']['stubs'] = makeList($files, '');

        // check for running PHP
        $stats['PHP']['binary']                 = phpversion();
        $stats['PHP']['memory_limit']           = ini_get('memory_limit');
        $stats['PHP']['short_open_tags']        = (ini_get('short_open_tags') ? 'On' : 'Off');
        $stats['PHP']['ext/curl']               = extension_loaded('curl') ? 'Yes' : 'No (Compulsory, please install it with --with-curl)';
        $stats['PHP']['ext/hash']               = extension_loaded('hash') ? 'Yes' : 'No (Compulsory, please install it with --enable-hash)';
        $stats['PHP']['ext/phar']               = extension_loaded('phar') ? 'Yes' : 'No (Needed to run exakat.phar. please install by default)';
        $stats['PHP']['ext/sqlite3']            = extension_loaded('sqlite3') ? 'Yes' : 'No (Compulsory, please install it by default (remove --without-sqlite3))';
        $stats['PHP']['ext/tokenizer']          = extension_loaded('tokenizer') ? 'Yes' : 'No (Compulsory, please install it by default (remove --disable-tokenizer))';
        $stats['PHP']['ext/mbstring']           = extension_loaded('mbstring') ? 'Yes' : 'No (Compulsory, add --enable-mbstring to configure)';
        $stats['PHP']['ext/json']               = extension_loaded('json') ? 'Yes' : 'No';
        $stats['PHP']['ext/xmlwriter']          = extension_loaded('xmlwriter') ? 'Yes' : 'No (Optional, used by XML reports)';

        if (extension_loaded('xdebug') === true) {
            $stats['PHP']['xdebug.max_nesting_level']            = (ini_get('xdebug.max_nesting_level') ) . ' (Must be -1 or more than 1000)';
        }
        $stats['PHP']['pcre.jit']               = (ini_get('pcre.jit') ? 'On' : 'Off') . ' (Must be off on PHP 7.3 and OSX)';

        // java
        $res = shell_exec('java -version 2>&1');
        if (stripos($res, 'command not found') !== false) {
            $stats['java']['installed'] = 'No';
            $stats['java']['installation'] = 'No java found. Please, install Java Runtime (SRE) 1.7 or above from java.com web site.';
        } elseif (preg_match('/(java|openjdk) version "(.*)"/is', $res, $r)) {
            $lines = explode(PHP_EOL, $res);
            $line2 = $lines[1];
            $stats['java']['installed'] = 'Yes';
            $stats['java']['type'] = trim($line2);
            $stats['java']['version'] = $r[1];
        } else {
            $stats['java']['error'] = $res;
            $stats['java']['installation'] = 'No java found. Please, install Java Runtime (SRE) 1.7 or above from java.com web site.';
        }
        $stats['java']['$JAVA_HOME'] = getenv('JAVA_HOME') ? getenv('JAVA_HOME') : '<none>';
        $stats['java']['$JAVA_OPTIONS'] = getenv('JAVA_OPTIONS') ?? ' (set $JAVA_OPTIONS="-Xms32m -Xmx****m", with **** = RAM in Mb. The more the better.';

        $stats['tinkergraph']   = Graph::getConnexion('Tinkergraph')->getInfo();
        $stats['tinkergraphv3'] = Graph::getConnexion('TinkergraphV3')->getInfo();
        $stats['gsneo4j']       = Graph::getConnexion('GSNeo4j')->getInfo();
        $stats['gsneo4jv3']     = Graph::getConnexion('GSNeo4jV3')->getInfo();
        $stats['nogremlin']     = Graph::getConnexion('NoGremlin')->getInfo();

        if ($this->config->project !== null) {
            $stats['project']['name']             = $this->config->project_name;
            $stats['project']['url']              = $this->config->project_url;
            $stats['project']['phpversion']       = $this->config->phpversion;
            $stats['project']['reports']          = makeList($this->reportList);
            $stats['project']['rulesets']         = makeList($this->config->project_rulesets  ?? array(), '');
            $stats['project']['included dirs']    = makeList($this->config->include_dirs      ?? array(), '');
            $stats['project']['ignored dirs']     = makeList($this->config->ignore_dirs       ?? array(), '');
            $stats['project']['ignored rules']    = makeList($this->config->ignore_rules      ?? array(), '');
            $stats['project']['file extensions']  = makeList($this->config->file_extensions   ?? array(), '');
        }

        return $stats;
    }

    private function checkAutoInstall(): array {
        $stats = array();

        // config
        if (!file_exists("{$this->config->projects_root}/config")) {
            mkdir("{$this->config->projects_root}/config", 0755);
        }

        if (file_exists("{$this->config->projects_root}/config/exakat.ini")) {
            $graphdb = $this->config->graphdb;
            $folder = '';
        } else {
            $ini = file_get_contents("{$this->config->dir_root}/server/exakat.ini");
            $version = PHP_MAJOR_VERSION . PHP_MINOR_VERSION;

            if (file_exists("{$this->config->projects_root}/tinkergraph")) {
                // This is the default expected folder
                $folder = 'tinkergraph';
                // tinkergraph or gsneo4j
                if (file_exists("{$this->config->projects_root}/tinkergraph/ext/neo4j-gremlin/")) {
                    $graphdb = 'gsneo4jv3';
                } else {
                    $graphdb = 'tinkergraphv3';
                }
            } else {
                $folder = '';
                $graphdb = 'nogremlin';
            }

            $ini = str_replace(array('{VERSION}', '{VERSION_PATH}',   '{GRAPHDB}', ";$graphdb", '{GRAPHDB}_path', ),
                               array( $version,    $this->config->php, $graphdb,    $graphdb,    $folder),
                               $ini);

            file_put_contents("{$this->config->projects_root}/config/exakat.ini", $ini);
        }

        $this->checkInstall($graphdb);

        // projects
        if (file_exists("{$this->config->projects_root}/projects/")) {
            $stats['folders']['projects folder'] = 'Yes';
        } else {
            mkdir("{$this->config->projects_root}/projects/", 0755);
            if (file_exists("{$this->config->projects_root}/projects/")) {
                $stats['folders']['projects folder'] = 'Yes';
            } else {
                $stats['folders']['projects folder'] = 'No';
            }
        }

        // stubs
        if (!file_exists($this->config->dir_root . '/stubs')) {
//            mkdir($this->config->dir_root . '/stubs', 0755);
        }

        // projects
        if (file_exists('./projects') &&
            !file_exists("{$this->config->projects_root}/projects/test")) {

            $i = 0;
            do {
                ++$i;
                $id = random_int(0, PHP_INT_MAX);
            } while (file_exists("{$this->config->projects_root}/projects/test$id") && $i < 100);

            shell_exec('php exakat init -p test' . $id . '');

            rename("{$this->config->projects_root}/projects/test$id", "{$this->config->projects_root}/projects/test");
            unset($init);
            unset($initConfig);
        }

        return $stats;
    }

    private function checkInstall(string $graphdb): void {
        if ($graphdb === 'gsneo4j') {
            if (file_exists("{$this->config->projects_root}/{$this->config->gsneo4j_folder}/conf/neo4j-empty.properties")) {
                $properties = file_get_contents("{$this->config->projects_root}/{$this->config->gsneo4j_folder}/conf/neo4j-empty.properties");
                $properties = preg_replace("#gremlin.neo4j.directory=.*\n#s", "gremlin.neo4j.directory=db/neo4j\n", $properties);
                file_put_contents("{$this->config->projects_root}/{$this->config->gsneo4j_folder}/conf/neo4j-empty.properties", $properties);
            }

            $this->checkGremlinServer("{$this->config->projects_root}/{$this->config->gsneo4j_folder}");
        } elseif ($graphdb === 'tinkergraph') {
            $this->checkGremlinServer("{$this->config->projects_root}/{$this->config->tinkergraph_folder}");
        } elseif ($graphdb === 'nogremlin') {
            // Nothing to do
        }
    }

    private function checkGremlinServer(string $path): void {
        if (!file_exists($path)) {
            return;
        }

        if (!file_exists("$path/db")) {
            mkdir("$path/db", 0755);
        }

        $gremlinJar = glob("{$this->config->gsneo4j_folder}/lib/gremlin-core-*.jar");
        $gremlinVersion = empty($gremlinJar) ? '' : substr(basename(array_pop($gremlinJar)), 13, -4);

        if (version_compare('3.4.0', $gremlinVersion) < 0) {
            $version = '.3.4';
        } elseif (version_compare('3.3.0', $gremlinVersion) < 0) {
            $version = '.3.3';
        } elseif (version_compare('3.2.0', $gremlinVersion) < 0) {
            $version = '.3.2';
        } else {
            print "Warning : Wrong Gremlin version found : $gremlinVersion read. Possible version range from 3.2.0 to 3.4.0.";
            return;
        }

        if (!copy("{$this->config->dir_root}/server/gsneo4j/gsneo4j{$version}.yaml",
             "$path/conf/gsneo4j.yaml")) {
            display("Error while copying gsneo4j{$version}.yaml config file to tinkergraph.");
        }
        if (!copy("{$this->config->dir_root}/server/tinkergraph/tinkergraph{$version}.yaml",
             "$path/tinkergraph.yaml")) {
            display("Error while copying tinkergraph{$version}.yaml config file to tinkergraph.");
        }
    }

    private function checkPHPs(array $config): array {
        $stats = array();

        foreach(Config::PHP_VERSIONS as $shortVersion) {
            $configVersion = "php$shortVersion";
            $version = "$shortVersion[0].$shortVersion[1]";
            if (isset($config[$configVersion])) {
                $stats[$configVersion] = $this->checkPHP($config[$configVersion], $version);
            }
        }

        return $stats;
    }

    private function checkOptional(): array {
        $stats = array();

        $optionals = array('Git'       => 'git',
                           'Mercurial' => 'hg',
                           'Svn'       => 'svn',
                           'Cvs'       => 'cvs',
                           'Bazaar'    => 'bzr',
                           'Composer'  => 'composer',
                           'Zip'       => 'zip',
                           'Rar'       => 'rar',
                           'Tarbz'     => 'tbz',
                           'Targz'     => 'tgz',
                           'SevenZ'    => '7z',
                          );

        foreach($optionals as $class => $section) {
            try {
                $fullClass = "\Exakat\Vcs\\$class";
                $vcs = new $fullClass($this->config->project, $this->config->code_dir);
                $stats[$section] = $vcs->getInstallationInfo();
            } catch (HelperException $e) {
                $stats[$section] = array('installed' => 'No');
            }
        }

        return $stats;
    }

    private function checkPHP(string $pathToBinary, string $displayedVersion): array {
        $stats = array();

        $stats['configured'] = 'Yes (' . $pathToBinary . ')';

        try {
            $php = new Phpexec($displayedVersion, $pathToBinary);
            $stats['actual version'] = $php->getActualVersion();
            if (substr($stats['actual version'], 0, 3) === $this->config->phpversion) {
                $stats['auditing'] = 'with this version';
            }
        } catch (NoPhpBinary $e) {
            $stats['installed'] = 'Invalid path : ' . $pathToBinary;
        }
        return $stats;
    }

    private function array2list(array $array): string {
        return implode(",\n                           ", $array);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Config;
use Exakat\Exceptions\AnotherProcessIsRunning;
use Exakat\Exceptions\ProjectTooLarge;
use Exakat\Log;

abstract class Tasks {
    protected $log        = null;
    protected $logname    = self::LOG_AUTONAMING;
    protected $datastore  = null;

    protected $gremlin    = null;
    protected $config     = null;

    protected $is_subtask   = self::IS_NOT_SUBTASK;

    protected static $semaphore      = null;
    protected static $semaphorePort  = null;

    protected $rulesets = null;

    private $snitch = null;
    private $pid = 0;
    private $path = '';

    public const  NONE    = 1;
    public const  ANYTIME = 2;
    public const  DUMP    = 3;
    public const  QUEUE   = 4;
    public const  SERVER  = 5;

    public const IS_SUBTASK     = true;
    public const IS_NOT_SUBTASK = false;

    public const LOG_NONE = null;
    public const LOG_AUTONAMING = '';

    public function __construct(bool $subTask = self::IS_NOT_SUBTASK) {
        $this->gremlin    = exakat('graphdb');
        $this->config     = exakat('config');
        $this->datastore  = exakat('datastore');
        $this->datastore->reuse();
        $this->is_subtask = $subTask;

        assert(defined('static::CONCURENCE'), get_class($this) . " is missing CONCURENCE\n");

        if (static::CONCURENCE !== self::ANYTIME && $subTask === self::IS_NOT_SUBTASK) {
            if (self::$semaphore === null) {
                if (static::CONCURENCE === self::QUEUE) {
                    self::$semaphorePort = $this->config->concurencyCheck;
                } elseif (static::CONCURENCE === self::SERVER) {
                    self::$semaphorePort = $this->config->concurencyCheck + 1;
                } elseif (static::CONCURENCE === self::DUMP) {
                    self::$semaphorePort = $this->config->concurencyCheck + 2;
                } else {
                    self::$semaphorePort = $this->config->concurencyCheck + 3;
                }

                if ($socket = @stream_socket_server('udp://0.0.0.0:' . self::$semaphorePort, $errno, $errstr, STREAM_SERVER_BIND)) {
                    self::$semaphore = $socket;
                } else {
                    throw new AnotherProcessIsRunning();
                }
            }
        }

        if ($this->logname === self::LOG_AUTONAMING) {
            $a = get_class($this);
            $this->logname = strtolower(substr($a, strrpos($a, '\\') + 1));
        }

        if ($this->logname !== self::LOG_NONE) {
            $this->log = new Log($this->logname,
                                 "{$this->config->projects_root}/projects/{$this->config->project}");
        }

        if ($this->config->inside_code === Config::INSIDE_CODE ||
            $this->config->project !== 'default') {
                if (!file_exists($this->config->tmp_dir) &&
                     file_exists(dirname($this->config->tmp_dir)) ) {
                    mkdir($this->config->tmp_dir, 0700);
            }
        } elseif (!file_exists("{$this->config->projects_root}/projects/")) {
            mkdir("{$this->config->projects_root}/projects/", 0700);
        }

        if ($this->config->project !== 'default') {
            $this->datastore = exakat('datastore');
        }

        $this->rulesets = exakat('rulesets');
    }

    public function __destruct() {
        if (static::CONCURENCE !== self::ANYTIME       &&
            $this->is_subtask === self::IS_NOT_SUBTASK &&
            !empty(self::$semaphore)) {
            fclose(self::$semaphore);
            self::$semaphore = null;
            self::$semaphorePort = -1;
        }
    }

    protected function checkTokenLimit(): void {
        $nb_tokens = $this->datastore->getHash('tokens');

        if ($nb_tokens > $this->config->token_limit) {
            $this->datastore->addRow('hash', array('token error' => "Project too large ($nb_tokens / {$this->config->token_limit})"));
            throw new ProjectTooLarge($nb_tokens, $this->config->token_limit);
        }
    }

    abstract public function run(): void;

    protected function cleanLogForProject(): void {
        $logs = glob("{$this->config->log_dir}/*");
        foreach($logs as $log) {
            unlink($log);
        }
    }

    protected function addSnitch(array $values = array()): void {
        if ($this->snitch === null) {
            $this->snitch = str_replace('Exakat\\Tasks\\', '', get_class($this));
            $this->pid = getmypid();
            $this->path = "{$this->config->tmp_dir}/{$this->snitch}.json";
        }

        $values['pid'] = $this->pid;
        file_put_contents($this->path, json_encode($values));
    }

    protected function removeSnitch(): void {
        if ($this->path !== null) {
            unlink($this->path);
        }
    }

    public function setConfig(Config $config): void {
        $this->config = $config;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\MissingFile;
use Exakat\Exceptions\NoCodeInProject;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Exceptions\NoSuchProject;

class FindExternalLibraries extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public const WHOLE_DIR   = 1;
    public const FILE_ONLY   = 2;
    public const PARENT_DIR  = 3; // Whole_dir and parent.

    private $php               = null;
    private $phpTokens         = array();
    private $whiteSpace        = array();

    private $classicTestsNames = array();
    private $classicTests      = array();
    private $classic           = array();

    public function __construct(bool $subTask = self::IS_NOT_SUBTASK) {
        parent::__construct($subTask);

        $json = json_decode(file_get_contents("{$this->config->dir_root}/data/externallibraries.json"));
        foreach((array) $json as $name => $o) {
            if ($o->type === 'classic') {
                foreach($o->classes as $class) {
                    $this->classic[$class] = constant("self::$o->ignore");
                }
            } elseif ($o->type === 'test') {
                foreach($o->classes as $class) {
                    $this->classicTests[$class] = constant("self::$o->ignore");
                    $this->classicTestsNames[$class] = $o->name;
                }
            } else {
                assert(false, "[External libraries] : Wrong type for $name : $o->type\n");
            }
        }
    }

    public function run(): void {
        $project = $this->config->project;
        if ($project === 'default') {
            throw new ProjectNeeded();
        }

        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($project);
        }

        if (!file_exists($this->config->code_dir)) {
            throw new NoCodeInProject($project);
        }

        $cacheFile = "{$this->config->project_dir}/config.cache";

        display('Processing files');
        Files::findFiles($this->config->code_dir, $files, $ignoredFiles, $this->config);

        if (empty($files)) {
            display('No files to process. Aborting');
            return;
        }

        $missing = array();
        foreach($files as $file) {
            if (!file_exists($this->config->code_dir . $file)) {
                $missing[] = $file;
            }
        }
        if (!empty($missing)) {
            throw new MissingFile($missing);
        }

        $this->php = exakat('php');

        $this->phpTokens = array_flip($this->php->getTokens());
        $this->whiteSpace = array($this->phpTokens['T_WHITESPACE']  => 1,
                                  $this->phpTokens['T_DOC_COMMENT'] => 1,
                                  $this->phpTokens['T_COMMENT']     => 1,
                                 );

        rsort($files);
        $ignore = 'None';
        $ignoreLength = 0;
        $regex = '$^(' . implode('|', $this->config->include_dirs) . ')$';
        $toCheckFiles = preg_grep($regex, $files, PREG_GREP_INVERT);

        foreach($toCheckFiles as $file) {
            if (substr($file, 0, $ignoreLength) === $ignore) {
                display( "Ignore $file ($ignore)\n");
                continue;
            }
            $this->process($file);
        }

        $newConfigs = $this->config->include_dirs;

        if (count($newConfigs) === 1) {
            display('One external library is going to be omitted : ' . implode(', ', array_keys($newConfigs)));
        } elseif (!empty($newConfigs)) {
            display(count($newConfigs) . ' external libraries are going to be omitted : ' . implode(', ', array_keys($newConfigs)));
        }

        $store = array();
        foreach($newConfigs as $library => $file) {
            $store[] = compact('library', 'file');
        }

        $this->datastore->cleanTable('externallibraries');
        $this->datastore->addRow('externallibraries', $store);

        if ($this->config->update === true) {
            if (file_exists($cacheFile)) {
                display("$project has already a file cache. Omitting.");
                return; //Cancel task
            }

             display("'Updating $project/config.cache");
             $ini = '; This file contains configuration auto-generated by exakat. ' . PHP_EOL .
                    '; Do not edit this file manually : in case of doubt, remove it to regenerate it. ' . PHP_EOL .
                    '; This file was auto-generated on ' . date('r') . PHP_EOL;
            if (empty($newConfigs)) {
                $ini .= PHP_EOL . '; This file is intentionally left blank' . PHP_EOL;
            } else {
                $ini .= PHP_EOL . '; This file is contains ' . count($newConfigs) . ' lines' . PHP_EOL
                               . 'ignore_dirs[] = ' . implode("\n" . 'ignore_dirs[] = ', $newConfigs) . PHP_EOL;
            }

             file_put_contents($cacheFile, $ini);
        } else {
            display('Not updating ' . $project . '/config.cache. ' . count($newConfigs) . ' external libraries found');
        }
    }

    private function process(string $filename): void {
        $return = array();

        $tokens = $this->php->getTokenFromFile($filename);
        if (count($tokens) === 1) {
            return ;
        }
        $this->log->log("$filename : " . count($tokens));

        foreach($tokens as $id => $token) {
            if (is_string($token)) { continue; }

            if (isset($this->whiteSpace[$token[0]])) { continue; }

            // If we find a namespace, it is not the global space, and we may skip the rest.
            if ($token[0] === $this->phpTokens['T_NAMESPACE']) {
                return ;
            }

            if ($token[0] === $this->phpTokens['T_CLASS']) {
                if (!isset($tokens[$id + 2]) ||
                    !is_array($tokens[$id + 2])) { continue; }
                $class = $tokens[$id + 2][1];
                if (!is_string($class)) {
                    // ignoring errors in the parsed code. Should go to log.
                    continue;
                }

                $lclass = strtolower($class);
                $returnPath = '';
                if (isset($this->classic[$lclass])) {
                    if ($this->classic[$lclass] === static::WHOLE_DIR) {
                        $returnPath = dirname(preg_replace('#.*projects/.*?/code/#', '/', $filename));
                    } elseif ($this->classic[$lclass] === static::PARENT_DIR) {
                        $returnPath = dirname(preg_replace('#.*projects/.*?/code/#', '/', $filename), 2);
                    } elseif ($this->classic[$lclass] === static::FILE_ONLY) {
                        $returnPath = preg_replace('#.*projects/.*?/code/#', '/', $filename);
                    }
                    if ($returnPath != '/') {
                        $return[$class] = $returnPath;
                    }
                }

                if (isset($tokens[$id + 4])    &&
                    is_array($tokens[$id + 4]) &&
                    $tokens[$id + 4][0] === $this->phpTokens['T_EXTENDS']) {
                    $ix = $id + 6;
                    $extends = '';

                    while($tokens[$ix][0] === T_NS_SEPARATOR || $tokens[$ix][0] === $this->phpTokens['T_STRING'] ) {
                        $extends .= $tokens[$ix][1];
                        ++$ix;
                    }

                    $extends = strtolower(trim($extends, '\\'));
                    if (isset($this->classicTests[$extends])) {
                        if ($this->classicTests[$extends] === static::WHOLE_DIR) {
                            $returnPath = dirname(preg_replace('#.*projects/.*?/code/#', '/', $filename));
                        } elseif ($this->classicTests[$extends] === static::PARENT_DIR) {
                            $returnPath = dirname(preg_replace('#.*projects/.*?/code/#', '/', $filename), 2);
                        } elseif ($this->classicTests[$extends] === static::FILE_ONLY) {
                            $returnPath = preg_replace('#.*projects/.*?/code/#', '/', $filename);
                        }
                        if ($returnPath !== '/') {
                            $class = $this->classicTestsNames[$extends];
                            $return[$class] = $returnPath;
                        }
                    }
                }
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Configsource\ProjectConfig;
use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Exceptions\VcsError;
use Exakat\Project;
use Exakat\Vcs\Vcs;
use Exakat\Vcs\None;

class Initproject extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        if ($this->config->project === 'default') {
            throw new ProjectNeeded();
        }

        if ($this->config->project === 'test') {
            throw new InvalidProjectName('Can\t use test as project name.');
        }

        if (!$this->config->project->validate()) {
            throw new InvalidProjectName($this->config->project->getError());
        }

        $repositoryURL = $this->config->repository;

        if ($this->config->delete === true) {
            display("Deleting {$this->config->project}");

            // final wait..., just in case
            sleep(2);

            rmdirRecursive("{$this->config->projects_root}/projects/{$this->config->project}");
        }

        display("Initializing {$this->config->project}" . (!empty($repositoryURL) ? " with $repositoryURL" : '') );
        $this->initProject($this->config->project, $repositoryURL ?: '');

        display('Done');
    }

    private function initProject(Project $project, string $repositoryURL): void {
        $finalPath = "{$this->config->projects_root}/projects/$project";

        if (file_exists($finalPath)) {
            display( "$finalPath already exists. Reusing it.\n");

            return;
        }

        $tmpPath = "{$this->config->projects_root}/projects/$project";
        if (file_exists($tmpPath)) {
            display("Removing tmpPath : $tmpPath\n");
            rmdirRecursive($tmpPath);
        }

        if (!file_exists("{$this->config->projects_root}/projects/")) {
            mkdir("{$this->config->projects_root}/projects/", 0755);
        }

        if (!mkdir($tmpPath, 0755)) {
            die("Could not create project directory '$project'");
        }

        if (!mkdir("{$tmpPath}/log/", 0755)) {
            die("Could not finalyze project directory '$project'");
        }

        $repositoryBranch    = '';
        $repositoryTag       = '';
        $include_dirs        = $this->config->include_dirs;

        $dotProject          = ".$project";
        if (empty($repositoryURL)) {
            $vcs = new None($dotProject, "$tmpPath/code");
            $projectName = $project;
        } else {
            $vcsClass = Vcs::getVcs($this->config);
            $vcs = new $vcsClass($dotProject, "$tmpPath/code");

            switch($vcs->getName()) {
                case 'symlink' :
                    $projectName = basename($repositoryURL);
                    break;

                case 'svn' :
                    $projectName = basename($repositoryURL);
                    if (in_array($projectName, array('trunk', 'code'))) {
                        $projectName = basename(dirname($repositoryURL));
                        if (in_array($projectName, array('trunk', 'code'))) {
                            $projectName = basename(dirname($repositoryURL, 2));
                        }
                    }
                    break;

                case 'git' :
                    $projectName = basename($repositoryURL);
                    $projectName = str_replace('.git', '', $projectName);

                    if (!empty($this->config->branch) &&
                        $this->config->branch !== 'master') {
                        $repositoryBranch =  $this->config->branch;
                        $repositoryTag =  '';
                    } elseif (!empty($this->config->tag)) {
                        $repositoryBranch =  '';
                        $repositoryTag =  $this->config->tag;
                    } else {
                        $repositoryBranch =  '';
                        $repositoryTag =  '';
                    }
                    break;

                case 'cvs' :
                    $projectName = basename($repositoryURL);
                    break;

                case 'copy' :
                    $projectName = basename($repositoryURL);
                    break;

                case 'mercurial' :
                    $projectName = basename($repositoryURL);
                    break;

                case 'bazaar' :
                    list(, $projectName) = explode(':', $repositoryURL, 3);
                    break;

                case 'zip' :
                    $projectName = basename($repositoryURL);
                    $projectName = str_replace('.zip', '', $projectName);
                    break;

                case 'rar' :
                    $projectName = basename($repositoryURL);
                    $projectName = str_replace('.rar', '', $projectName);
                    break;

                case 'targz' :
                    $projectName = basename($repositoryURL);
                    $projectName = str_replace(array('.tgz', '.tar.gz'), '', $projectName);
                    break;

                case 'tarbz' :
                    $projectName = basename($repositoryURL);
                    $projectName = str_replace(array('.tbz', '.tar.bz'), '', $projectName);
                    break;

                case 'composer' :
                    $projectName = str_replace('/', '_', $repositoryURL);

                    // Updating config.ini to include the vendor directory
                    $include_dirs[] = "/vendor/$repositoryURL";
                    break;

                default :
                    $projectName = basename($repositoryURL);
                    $projectName = str_replace('/\.git/', '', $projectName);
                    break;
            }
        }

        // default initial config. Found in test project.
        $phpversion = $this->config->phpversion;
        if ($this->config->composer === true) {
            $ignore_dirs = $this->config->ignore_dirs;
        } else {
            $ignore_dirs = array_merge($this->config->ignore_dirs, array('/vendor'));
        }

        $projectConfig = new ProjectConfig($this->config->projects_root);
        $projectConfig->setProject($project);
        $projectConfig->setConfig('phpversion',     $phpversion);
        $projectConfig->setConfig('project_name',   $projectName);
        $projectConfig->setConfig('project_url',    $repositoryURL);
        $projectConfig->setConfig('project_vcs',    $vcs->getName());
        $projectConfig->setConfig('project_tag',    $repositoryTag);
        $projectConfig->setConfig('project_branch', $repositoryBranch);

        $projectConfig->setConfig('ignore_dirs',    $ignore_dirs);
        $projectConfig->setConfig('include_dirs',   $include_dirs);

        shell_exec("chmod -R g+w $tmpPath");

        if (!empty($this->config->branch)){
            $vcs->setBranch($this->config->branch);
        }

        if (!empty($this->config->tag)){
            $vcs->setTag($this->config->tag);
        }

        try {
            $vcs->clone($repositoryURL);
        } catch (VcsError $e) {
            rename($tmpPath, $finalPath);

            $this->datastore = exakat('datastore');
            $this->datastore->create();

            $errorMessage = $e->getMessage();
            $this->datastore->addRow('hash', array('init error' => $errorMessage,
                                                   'inited'     => date('r')));
            print "An error prevented code initialization: no code was loaded.\n.Error : $errorMessage'\n";

            file_put_contents("{$this->config->project_dir}/config.ini", $projectConfig->getConfig($this->config->dir_root));

            return;
        }

        rename($tmpPath, $finalPath);
        file_put_contents("{$this->config->project_dir}/config.ini", $projectConfig->getConfig($this->config->dir_root));
        $this->datastore = exakat('datastore');
        $this->datastore->create();

        $this->datastore->addRow('hash', array('status' => 'Cloned',
                                              ));

        $this->datastore->addRow('hash', array('status' => 'Initproject',
                                               'inited' => date('r')));
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Configsource\ProjectConfig;
use Exakat\Configsource\DotExakatYamlConfig;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Config as Configuration;

class Config extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        $project = $this->config->project;

        // May be in-code!!
        if ($this->config->inside_code === Configuration::INSIDE_CODE) {
            $projectConfig = new DotExakatYamlConfig($this->config->project, $this->config->projects_root);
            $projectConfig->loadConfig($project);
        } elseif ($this->config->project === null) {
            $projectConfig = new ProjectConfig($this->config->projects_root);
        } else {
            if (!file_exists("{$this->config->projects_root}/projects/$project")) {
                throw new NoSuchProject($this->config->project);
            }

            $projectConfig = new ProjectConfig($this->config->projects_root);
            $projectConfig->loadConfig($project);
        }

        print $projectConfig->getConfig($this->config->dir_root);
    }
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\NoJobqueueStarted;

class Queue extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    private $pipefile = Jobqueue::PATH;

    public function run(): void {
        if (!file_exists($this->pipefile)) {
            throw new NoJobqueueStarted();
        }

        if ($this->config->stop === true) {
            display('Stopping queue');
            $queuePipe = fopen($this->pipefile, 'w');
            fwrite($queuePipe, "quit\n");
            fclose($queuePipe);

            return;
        }

        if ($this->config->ping === true) {
            display('Ping queue');
            $queuePipe = fopen($this->pipefile, 'w');
            fwrite($queuePipe, "ping\n");
            fclose($queuePipe);

            return;
        }

        $queue = $this->config->commandlineJson();
        $queuePipe = fopen($this->pipefile, 'w');
        fwrite($queuePipe, "$queue\n");
        fclose($queuePipe);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Config;
use Exakat\Exakat;

class Upgrade extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        // Avoid downloading when it is not a phar
        if ($this->config->is_phar === Config::IS_NOT_PHAR) {
            print 'This can only update a .phar version of exakat. Aborting.' . PHP_EOL;
            return;
        }

        $options = array(
            'http'=>array(
                'method' => 'GET',
                'header' => 'User-Agent: exakat-' . Exakat::VERSION
            )
        );

        $context = stream_context_create($options);
        $html = file_get_contents('https://www.exakat.io/versions/index.php', true, $context);

        if (empty($html)) {
            print 'Unable to reach server to fetch the last version. Try again later.' . PHP_EOL;
            return;
        }

        if (empty($this->config->version)) {
            if (preg_match('/Download exakat version (\d+\.\d+\.\d+) \(Latest\)/s', $html, $r) == 0) {
                print 'Unable to find the requested version. Make sure the version number is valid. ' . PHP_EOL;
                return;
            }

            $version = $r[1];
        } else {
            $version = $this->config->version;
            if (preg_match('/^\d+\.\d+\.\d+$/s', $version, $r) == 0) {
                print 'Version number could not be recognized. Remove the option -version, or provide a valid version number, like "1.8.7".' . PHP_EOL;
                return;
            }

            if (preg_match('/>exakat-' . $version . '.phar<\/a>/s', $html) !== 1) {
                print 'Unable to find last version. Try again later.' . PHP_EOL;
                return;
            }
        }

        if (version_compare(Exakat::VERSION, $version) !== 0) {
            echo 'This version may be updated from the current version ' , Exakat::VERSION , ' to ' , $version  , PHP_EOL;

            if ($this->config->update === true) {

                echo '  Updating to version ' , $version , PHP_EOL;
                preg_match('#<pre id="sha256"><a href="index.php\?file=exakat-' . $version . '.phar.sha256">(.*?)</pre>#', $html, $r);
                $sha256 = strip_tags($r[1]);

                // Read what we can
                $phar = (string) @file_get_contents('https://www.exakat.io/versions/index.php?file=exakat-' . $version . '.phar');

                if (hash('sha256', $phar) !== $sha256) {
                    print 'Error while checking exakat.phar\'s checksum. Aborting update. Please, try again' . PHP_EOL;
                    return;
                }

                $path = sys_get_temp_dir() . '/exakat.1.phar';
                file_put_contents($path, $phar);
                print 'Setting up exakat.phar' . PHP_EOL;
                rename($path, 'exakat.phar');

                return;
            } else {
                print '  You may run this command with -u option to upgrade to the latest exakat version.' . PHP_EOL;
                return;
            }
        } elseif (version_compare(Exakat::VERSION, $r[1]) === 0) {
            print 'This is the latest version (' . Exakat::VERSION . ')' . PHP_EOL;
            return;
        } else {
            print 'This version is ahead of the latest publication (Current : ' . Exakat::VERSION . ', Latest: ' . $r[1] . ')' . PHP_EOL;
            return;
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Config;
use Exakat\Exceptions\NoSuchFile;
use Exakat\Exceptions\NoSuchDir;

class TestCobble extends Tasks {
    public const CONCURENCE = self::NONE;

    public function run(): void {
        // Check for requested file
        if (!empty($this->config->filename) && !file_exists($this->config->filename[0])) {
            throw new NoSuchFile($this->config->filename[0]);
        } elseif (!empty($this->config->dirname) && !file_exists($this->config->dirname)) {
            throw new NoSuchDir($this->config->filename);
        }

        // Check for requested analyze
        $analyzerName = $this->config->program[0];
        /*
        if (!$this->rulesets->getClass($analyzerName)) {
            throw new NoSuchAnalyzer($analyzerName, $this->rulesets);
        }
        */

        display("Cleaning DB\n");
        $args = array ( 1 => 'cleandb',
                        2 => '-p',
                        3 => 'test',
                        4 => '-Q',
                        );
        $configThema = new Config($args);

        $analyze = new CleanDb(self::IS_SUBTASK);
        $analyze->setConfig($configThema);
        $analyze->run();

        display("Cleaning project\n");
        $clean = new Clean(self::IS_SUBTASK);
        $clean->run();

        $analyze = new Cobble(self::IS_SUBTASK);
        $analyze->run();
        unset($analyze);

        display("Analyzed project\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Analyzer\Rulesets;
use Exakat\Exakat;
use Exakat\Helpers\Timer;
use Exakat\Exceptions\MissingGremlin;
use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\NoCodeInProject;
use Exakat\Exceptions\NoFileToProcess;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\NoSuchReport;
use Exakat\Exceptions\ProjectNeeded;
use Exakat\Tasks\Helpers\BaselineStash;
use Exakat\Tasks\Helpers\ReportConfig;
use Exception;

use Exakat\Vcs\Vcs;

class Project extends Tasks {
    public const CONCURENCE = self::NONE;

    protected $rulesetsToRun = array('Analyze',
                                     'Preferences',
                                    );

    protected $reports       = array();
    protected $reportConfigs = array();
    private Timer $timer;

    public function __construct(bool $subTask = self::IS_NOT_SUBTASK) {
        parent::__construct($subTask);

        if (empty($this->reports)) {
            $this->reports = makeArray($this->config->project_reports);
        }

        $this->timer = new Timer();
    }

    public function run(): void {
        if ($this->config->project->isDefault()) {
            throw new ProjectNeeded();
        }

        if (!$this->config->project->validate()) {
            throw new InvalidProjectName($this->config->project->getError());
        }

        if ($this->config->gremlin === 'NoGremlin') {
            throw new MissingGremlin();
        }

        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject((string) $this->config->project);
        }

        if (!file_exists($this->config->code_dir)) {
            throw new NoCodeInProject((string) $this->config->project);
        }

        // Baseline is always the previous audit done, not the current one!
        $baselinestash = new BaselineStash($this->config);
        $baselinestash->copyPrevious($this->config->dump);

        display("Cleaning project\n");
        $clean = new Clean(self::IS_SUBTASK);
        $clean->run();
        $this->datastore = exakat('datastore');
        // Reset datastore for the others

        $this->logTime('Start');
        $this->addSnitch(array('step'    => 'Start',
                               'project' => $this->config->project));

        $audit_start = time();
        $this->datastore->addRow('hash', array('audit_start'      => $audit_start,
                                               'exakat_version'   => Exakat::VERSION,
                                               'exakat_build'     => Exakat::BUILD,
                                               'php_version'      => $this->config->phpversion,
                                               'audit_name'       => $this->generateName()
                                         ));

        $info = array();
        if (($vcsClass = Vcs::getVcs($this->config)) === 'None') {
            $info['vcs_type'] = 'Standalone archive';
        } else {
            $info['vcs_type'] = strtolower($vcsClass);
            $info['vcs_url']  = $this->config->project_url;

            $vcs = new $vcsClass($this->config->project, $this->config->code_dir);
            if (method_exists($vcs, 'getBranch')) {
                $info['vcs_branch']      = $vcs->getBranch();
            }
            if (method_exists($vcs, 'getRevision')) {
                $info['vcs_revision']      = $vcs->getRevision();
                $this->getLineDiff($info['vcs_revision'], $vcs);
            }
            $info['code_last_commit']      = $vcs->getLastCommitDate();
        }

        $info['stubs_config'] = json_encode($this->config->stubs);
        $this->datastore->addRow('hash', $info);

        $rulesetsToRun = array($this->config->project_rulesets);
        $namesToRun    = array();

        foreach($this->reports as $format) {
            try {
                $reportConfig = new ReportConfig($format, $this->config);
            } catch (NoSuchReport $e) {
                // Simple ignore
                display($e->getMessage());
                continue;
            }
            $this->reportConfigs[$reportConfig->getName()] = $reportConfig;

            $rulesets = $reportConfig->getRulesets();
            if (empty($rulesets)) {
                $rulesets = $reportConfig->getRulesets();
            }
            $rulesetsToRun[] = $rulesets;
            $namesToRun[] = $reportConfig->getName();

            unset($reportConfig);
            gc_collect_cycles();
        }

        $rulesetsToRun = array_merge(...$rulesetsToRun);
        $rulesetsToRun = array_filter($rulesetsToRun);
        $rulesetsToRun = array_unique($rulesetsToRun);

        $availableRulesets = $this->rulesets->listAllRulesets();
        $availableRulesets = array_map('strtolower', $availableRulesets);

        $diff = array();
        $rulesetsToRunShort = array();
        foreach($rulesetsToRun as $rule) {
            $rule = trim($rule, '"');
            if (in_array(strtolower($rule), $availableRulesets, \STRICT_COMPARISON)) {
                $rulesetsToRunShort[] = $rule;
            } else {
                $diff[] = $rule;
            }
        }

        if (!empty($diff)) {
            display('Ignoring the following unknown rulesets : ' . implode(', ', $diff) . PHP_EOL);
        }

        $rulesetsToRun = array_unique($rulesetsToRunShort);
        if (empty($rulesetsToRun)) {
            // Default values
            $rulesetsToRun = $this->rulesetsToRun;
        }

        display("Running project '" . (string) $this->config->project . "'" . PHP_EOL);
        display('Running the following rulesets : ' . implode(', ', $rulesetsToRun));
        display('Producing the following reports : ' . implode(', ', $namesToRun));

        display('Running files' . PHP_EOL);
        $analyze = new Files(self::IS_SUBTASK);
        $analyze->run();
        unset($analyze);
        $this->logTime('Files');
        $this->addSnitch(array('step'    => 'Files',
                               'project' => $this->config->project));

        $nb_files = (int) $this->datastore->getHash('files');
        if ($nb_files === 0) {
            throw new NoCodeInProject($this->config->project);
        }

        display('Cleaning DB' . PHP_EOL);
        $analyze = new CleanDb(self::IS_SUBTASK);
        $analyze->run();
        unset($analyze);
        $this->logTime('CleanDb');
        $this->addSnitch(array('step'    => 'Clean DB',
                               'project' => $this->config->project));
        $this->gremlin->init();

        $this->checkTokenLimit();

        $load = new Load(self::IS_SUBTASK);
        try {
            $load->run();
        } catch (NoFileToProcess $e) {
            $this->datastore->addRow('hash', array('init error' => $e->getMessage(),
                                                   'status'     => 'Error',
                                           ));
        }
        unset($load);
        display("Project loaded\n");
        $this->logTime('Loading');

        // Always run this one first
        $this->analyzeRulesets(array('First'), $audit_start, $this->config->verbose);

        // Dump is a child process
        // initialization and first collection (action done once)
        display('Initial dump');
        $dumpConfig = $this->config->duplicate(array('collect'            => true,
                                                     'load_dump'          => true,
                                                     'project_rulesets'   => array('First'),
                                                     'program'            => '',
                                                     )
                                                );
        $firstDump = new Dump(self::IS_SUBTASK);
        $firstDump->setConfig($dumpConfig);
        $firstDump->run();
        unset($firstDump);
        $this->logTime('Initial dump');

        if (empty($this->config->program)) {
            $this->analyzeRulesets($rulesetsToRun, $audit_start, $this->config->verbose);
        } else {
            $this->analyzeOne($this->config->program, $audit_start, $this->config->verbose);
        }

        display('Analyzed project' . PHP_EOL);
        $this->logTime('Analyze');
        $this->addSnitch(array('step'    => 'Analyzed',
                               'project' => $this->config->project));

        $this->logTime('Analyze');

        $dump = new Dump(self::IS_SUBTASK);
        foreach($this->config->rulesets as $name => $analyzers) {
            $dump->checkRulesets($name, $analyzers);
        }

        $this->logTime('Reports');
        if (empty($this->config->program)) {
            try {
                $report = new Report(self::IS_SUBTASK);

                $report->run();
            } catch (\Throwable $e) {
                display( 'Error while building the reports : ' . $e->getMessage() . "\n");
            }
        }
        display('Reported project' . PHP_EOL);

        // Reset cache from Rulesets
        Rulesets::resetCache();
        $this->logTime('Final');
        $this->removeSnitch();
        display('End' . PHP_EOL);
    }

    private function logTime(string $step): void {
        static $log;

        if ($log === null) {
            $log = fopen("{$this->config->log_dir}/project.timing.csv", 'w+');
        }

        $this->timer->end();

        fwrite($log, $step . "\t" . $this->timer->duration() . PHP_EOL);
        $this->timer = new Timer();
    }

    private function analyzeOne(array $analyzers, int $audit_start, bool $verbose): void {
        $this->addSnitch(array('step'    => 'Analyzer',
                               'project' => $this->config->project));

        try {
            $analyzeConfig = $this->config->duplicate(array('noRefresh' => true,
                                                            'update'    => true,
                                                            'program'   => $analyzers,
                                                            'verbose'   => $verbose,
                                                            'quiet'     => !$verbose,
                                                            ));

            $analyze = new Analyze(self::IS_SUBTASK);
            $analyze->run();
            unset($analyze);
            unset($analyzeConfig);
            $this->logTime('Analyze : ' . makeList($analyzers, ''));

            $dumpConfig = $this->config->duplicate(array('update'    => true,
                                                         'load_dump' => true,
                                                         'program'   => $analyzers,
                                                         ));

            $audit_end = time();
            $query = 'g.V().count()';
            $res = $this->gremlin->query($query);
            $nodes = $res[0];
            $query = 'g.E().count()';
            $res = $this->gremlin->query($query);
            $links = $res[0];

            $this->datastore->addRow('hash', array('audit_end'    => $audit_end,
                                                   'audit_length' => $audit_end - $audit_start,
                                                   'graphNodes'   => $nodes,
                                                   'graphLinks'   => $links));

            $dump = new Dump(self::IS_SUBTASK);
            $dump->run();
            unset($dump);
            unset($dumpConfig);
        } catch (\Exception $e) {
            echo "Error while running the Analyzer {$this->config->project}.\nTrying next analysis.\n";
            file_put_contents("{$this->config->log_dir}/analyze.final.log", $e->getMessage());
        }
    }

    private function analyzeRulesets($rulesets, int $audit_start,bool $verbose): void {
        if (empty($rulesets)) {
            $rulesets = $this->config->project_rulesets;
        }

        if (!is_array($rulesets)) {
            $rulesets = array($rulesets);
        }

        display('Running the following rulesets : ' . implode(', ', $rulesets) . PHP_EOL);

        global $VERBOSE;
        $oldVerbose = $VERBOSE;
        $VERBOSE = false;
        foreach($rulesets as $ruleset) {
            $this->addSnitch(array('step'    => 'Analyze : ' . $ruleset,
                                   'project' => $this->config->project));
            $rulesetForFile = strtolower(str_replace(' ', '_', trim($ruleset, '"')));

            try {
                $analyzeConfig = $this->config->duplicate(array('noRefresh'        => true,
                                                                'update'           => true,
                                                                'project_rulesets' => array($ruleset),
                                                                'verbose'          => $verbose,
                                                                'quiet'            => !$verbose,
                                                                ));

                $analyze = new Analyze(self::IS_SUBTASK);
                $analyze->setConfig($analyzeConfig);
                $analyze->run();
                unset($analyze);
                unset($analyzeConfig);
                $this->logTime("Analyze : $ruleset");

                $audit_end = time();
                $query = 'g.V().count()';
                $res = $this->gremlin->query($query);
                if (isset($res->results)) {
                    $nodes = $res->results[0];
                } else {
                    $nodes = $res[0];
                }
                $query = 'g.E().count()';
                $res = $this->gremlin->query($query);
                if (isset($res->results)) {
                    $links = $res->results[0];
                } else {
                    $links = $res[0];
                }

                $finalMark = array('audit_end'    => $audit_end,
                                   'audit_length' => $audit_end - $audit_start,
                                   'graphNodes'   => $nodes,
                                   'graphLinks'   => $links);
                $this->datastore->addRow('hash', $finalMark);

                // Skip Dump, as it is auto-saving itself.
                $dumpConfig = $this->config->duplicate(array('update'               => true,
                                                             'project_rulesets'     => array($ruleset),
                                                             'load_dump'            => true,
                                                             'verbose'              => false,
                                                             ));
/*
                $shell = "nohup php exakat dump -p {$this->config->project} -T $ruleset -load-dump -u >/dev/null & echo $!";
                $pid = shell_exec($shell);
                print "New PID : $pid\n";
                print "Slept for PID : $pid\n";
*/
                $dump = new Dump(self::IS_SUBTASK);
                $dump->setConfig($dumpConfig);
                $dump->run();
                $dump->finalMark($finalMark);
                unset($dump);
                unset($dumpConfig);

                gc_collect_cycles();
                $this->logTime("Dumped : $ruleset");
            } catch (Exception $e) {
                display("Error while running the ruleset $ruleset.\nTrying next ruleset.\n");
                file_put_contents("{$this->config->log_dir}/analyze.$rulesetForFile.final.log", $e->getMessage(), FILE_APPEND);
            }
        }
        $VERBOSE = $oldVerbose;
    }

    private function generateName(): string {
        $ini = parse_ini_file("{$this->config->dir_root}/data/audit_names.ini");

        $names = $ini['names'];
        $adjectives = $ini['adjectives'];

        shuffle($names);
        shuffle($adjectives);

        try {
            $x = random_int(0, PHP_INT_MAX);
        } catch (\Throwable $t) {
            $x = (int) microtime(true) * 1000000;
        }
        $name = $names[ $x % (count($names) - 1)];

        try {
            $x = random_int(0, PHP_INT_MAX);
        } catch (\Throwable $t) {
            $x = (int) microtime(true) * 1000000;
        }
        $adjective = $adjectives[ $x % (count($adjectives) - 1)];

        return ucfirst($adjective) . ' ' . $name;
    }

    private function getLineDiff(string $current, VCS $vcs): void {
        if ($this->config->dump_previous === null) {
            return ;
        }

        if (!file_exists($this->config->dump_previous)) {
            return ;
        }

        $sqlite = new \Sqlite3($this->config->dump_previous);
        $res = $sqlite->query('SELECT name FROM sqlite_master WHERE type="table" AND name="hash"');
        if ($res === false || !$res->numColumns() || $res->columnType(0) == SQLITE3_NULL) {
            return;
        }

        $res = $sqlite->query('SELECT value FROM hash WHERE key="vcs_revision"');
        if (!$res->numColumns() || $res->columnType(0) == SQLITE3_NULL) {
            return;
        }
        $revision = $res->fetchArray(\SQLITE3_ASSOC)['value'];

        $diff = $vcs->getDiffLines($revision, $current);
        if (!empty($diff)) {
            $this->datastore->addRow('linediff', $diff);
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Config;
use Exakat\Exceptions\NoSuchFile;
use Exakat\Exceptions\NoSuchDir;
use Exakat\Exceptions\NoSuchAnalyzer;

class Test extends Tasks {
    public const CONCURENCE = self::NONE;

    public function run(): void {
        // Check for requested file
        if (!empty($this->config->filename) && !file_exists($this->config->filename[0])) {
            throw new NoSuchFile($this->config->filename[0]);
        } elseif (!empty($this->config->dirname) && !file_exists($this->config->dirname)) {
            throw new NoSuchDir($this->config->filename);
        }

        // Check for requested analyze
        $analyzerName = $this->config->program[0];
        if (!$this->rulesets->getClass($analyzerName)) {
            throw new NoSuchAnalyzer($analyzerName, $this->rulesets);
        }

        display("Cleaning DB\n");
        $args = array ( 1 => 'cleandb',
                        2 => '-p',
                        3 => 'test',
                        4 => '-Q',
                        );
        $configThema = new Config($args);

        $analyze = new CleanDb(self::IS_SUBTASK);
        $analyze->setConfig($configThema);
        $analyze->run();

        display("Cleaning project\n");
        $clean = new Clean(self::IS_SUBTASK);
        $clean->run();

        $load = new Load(self::IS_SUBTASK);
        $load->run();
        unset($load);
        display("Project loaded\n");

        $analyze = new Analyze(self::IS_SUBTASK);
        $analyze->run();
        unset($analyze);

        display("Dumping results\n");
        $args = array ( 1 => 'dump',
                        2 => '-p',
                        3 => 'test',
                        4 => '--load-dump',
                        5 => '-u',
                        );
        $configThema = new Config($args);

        $analyze = new Dump(self::IS_SUBTASK);
        $analyze->setConfig($configThema);
        $analyze->run();

        $results = new Results(self::IS_SUBTASK);
        $results->run();
        unset($results);

        display("Analyzed project\n");
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

class Help extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {
        print <<<HELP
[Usage] :   php {$this->config->executable} version (default)
            php {$this->config->executable} doctor
            php {$this->config->executable} init -p <Project name> -R <Repository>
            php {$this->config->executable} project -p <Project name>

Check all commands : http://exakat.readthedocs.io/en/latest/Commands.html

HELP;

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\NoSuchProject;
use Exakat\Reports\Reports;
use Exakat\Vcs\Vcs;
use Exakat\Exakat;

class Status extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public const WITH_JSON    = 1;
    public const WITHOUT_JSON = 2;

    public function run(): void {
        $project = $this->config->project;

        if ($project->isDefault()) {
            $status = array();

            if (file_exists("{$this->config->tmp_dir}/Project.json")) {
                $json = file_get_contents("{$this->config->tmp_dir}/Project.json");
                if (empty($json)) {
                    $projectStatus = '';
                    $projectStep = '';
                } else {
                    $json = json_decode($json);
                    $projectStatus = $json->project;
                    $projectStep = $json->step;
                }

                $status = array('Running'  => 'Project',
                                'project'  => $projectStatus,
                                'step'     => $projectStep, );
            } else {
                $status['Running'] = 'idle';
            }

            $this->display($status, $this->config->json);
            return;
        }

        if (!file_exists($this->config->project_dir)) {
            throw new NoSuchProject($project);
        }

        if ($this->datastore->getHash('exakat_version') === null) {
            $this->datastore->create();
            $this->datastore->addRow('hash', array('exakat_version'  => Exakat::VERSION,
                                                   'exakat_build'    => Exakat::BUILD,
                                                   'php_version'     => $this->config->phpversion,
                                                   'file_extensions' => json_encode($this->config->file_extensions),
                                                   'ignore_dirs'     => json_encode($this->config->ignore_dirs),
                                                   'include_dirs'    => json_encode($this->config->include_dirs),
                                                   'vcs_url'         => $this->config->project_url,
                                                   'project'         => (string) $this->config->project,
                                                ));
        }

        $status = array('project'          => (string) $project,
                        'files'            => (int) ($this->datastore->getHash('files')         ?? 0),
                        'filesIgnored'     => (int) ($this->datastore->getHash('filesIgnored')  ?? 0),
                        'loc'              => (int) ($this->datastore->getHash('loc')           ?? 0),
                        'loc_all'          => (int) ($this->datastore->getHash('locTotal')      ?? 0),
                        'tokens'           => (int) ($this->datastore->getHash('tokens')        ?? 0),
                        'vcs'              => $this->datastore->getHash('vcs_type')      ?? '',
                        'url'              => $this->datastore->getHash('vcs_url')       ?? '',
                        'branch'           => $this->datastore->getHash('vcs_branch')    ?? '',
                        'revision'         => $this->datastore->getHash('vcs_revision')  ?? '',
                        'php'              => $this->datastore->getHash('php_version')   ?? '',
                        'include_dirs'     => join(', ', json_decode($this->datastore->getHash('include_dirs') ?? '[]')),
                        'ignore_dirs'      => join(', ', json_decode($this->datastore->getHash('ignore_dirs')  ?? '[]')),
                        'file_extensions'  => join(', ', json_decode($this->datastore->getHash('file_extensions')  ?? '[]')),
                        );
        if (file_exists("{$this->config->tmp_dir}/Project.json")) {
            $text = file_get_contents("{$this->config->tmp_dir}/Project.json");
            if (empty($text)) {
                 $inited = $this->datastore->getHash('inited');
                 $status['status'] = empty($inited) ? 'Init phase' : 'Not running';
            } else {
             $json = json_decode($text);
             if ($json->project === $project) {
                 $status['status'] = $json->step;
             } else {
                 $inited = $this->datastore->getHash('inited');
                 $status['status'] = empty($inited) ? 'Init phase' : 'Not running';
             }
            }
        } else {
            $inited = $this->datastore->getHash('inited');
            $status['status'] = empty($inited) ? 'Init phase' : 'Not running';
        }

        if (($vcsClass = Vcs::getVcs($this->config)) === 'None') {
            $status['hash']      = 'None';
            $status['updatable'] = 'N/A';
        } else {
            $vcs = new $vcsClass($this->config->project, $this->config->code_dir);
            $status = array_merge($status, $vcs->getStatus());
        }

        $status['updatable'] = $status['updatable'] === true ? 'Yes' : 'No';

        // Check the logs
        $errors = $this->getErrors($this->config->project_dir);
        if (!empty($errors)) {
            $status['errors'] = $errors;
        }

        // Status of progress
        // errors?

        $formats = array();
        foreach(Reports::$FORMATS as $format) {
            $a = $this->datastore->getHash($format);
            if (!empty($a)) {
                $formats[$format] = $a;
            }
        }
        // Always have formats, even if empty
        $status['formats'] = $formats;

        $this->display($status, $this->config->json);
    }

    private function display(array $status, bool $json = self::WITH_JSON) {
        // Json publication
        if ($json === self::WITH_JSON) {
            print json_encode($status);
            return;
        }

        // commandline publication
        $text = '';
        $size = 0;
        foreach($status as $k => $v) {
            $size = max($size, strlen($k));
        }

        foreach($status as $field => $value) {
            if (is_array($value)) {
                $sub = str_pad($field, $size, ' ') . ' : ' . PHP_EOL;

                $sizea = 0;
                foreach($value as $k => $v) {
                    $sizea = max($sizea, strlen($k));
                }
                foreach($value as $k => $v) {
                    $sub .= '    ' . str_pad($k, $sizea, ' ') . " : $v" . PHP_EOL;
                }
                $text .= PHP_EOL . $sub . PHP_EOL;
            } else {
                $text .= str_pad($field, $size, ' ') . ' : ' . $value . PHP_EOL;
            }
        }

        print $text;
    }

    private function getErrors(string $path): array {
        $errors = array();

        // Init error
        $e = $this->datastore->getHash('init error');
        if (!empty($e)) {
            $errors['init error'] = $e;
            return $errors;
        }

        // Size error
        $e = $this->datastore->getHash('token error');
        if (!empty($e)) {
            $errors['init error'] = $e;
            return $errors;
        }

        return $errors;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\ProjectNeeded;
use Exakat\Exceptions\NoSuchProject;

class Clean extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    protected $logname = self::LOG_NONE;

    private $filesToErase = array('Flat-html.html',
                                  'Flat-markdown.md',
                                  'Flat-sqlite.sqlite',
                                  'Flat-text.txt',
                                  'Premier-ace.zip',
                                  'Premier-html.html',
                                  'Premier-markdown.md',
                                  'Premier-sqlite.sqlite',
                                  'Premier-text.txt',
                                  'magicnumber.sqlite',
                                  'report.html',
                                  'report.md',
                                  'report.odt',
                                  'report.pdf',
                                  'report.json',
                                  'report.xml',
                                  'report.sqlite',
                                  'report.txt',
                                  'report.zip',
                                  'EchoWithConcat.json',
                                  'PhpFunctions.json',
                                  'bigArrays.txt',
                                  'counts.sqlite',
                                  'stats.txt',
                                  'faceted.zip',
                                  'faceted2.zip',

                                  'datastore.sqlite',
                                  'dump.sqlite',
                                 );

    public function run(): void {
        if ($this->config->project === 'default') {
            throw new ProjectNeeded();
        }

        if (!file_exists(dirname($this->config->code_dir))) {
            throw new NoSuchProject($this->config->project);
        }

        display( "Cleaning project {$this->config->project}\n");

        $dirsToErase = array('report',
                             'diplomat',
                             );
        foreach($dirsToErase as $dir) {
            $dirPath = "{$this->config->project_dir}/$dir";
            if (file_exists($dirPath)) {
                display("removing $dir");
                rmdirRecursive($dirPath);
            }
        }

        // rebuild tmp
        rmdirRecursive($this->config->tmp_dir);
        mkdir($this->config->tmp_dir, 0755);

        // rebuild log
        rmdirRecursive($this->config->log_dir);
        if (!file_exists($this->config->log_dir)) {
            mkdir($this->config->log_dir, 0755);
        }

        $total = 0;
        foreach($this->filesToErase as $file) {
            $filePath = "{$this->config->project_dir}/$file";
            if (file_exists($filePath)) {
                display("removing $file");
                unlink($filePath);
                ++$total;
            }
        }
        display("Removed $total files\n");

        $this->datastore->create();
        display("Recreating database\n");
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

class EmptyTask extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public function run(): void {

    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Exceptions\InvalidProjectName;
use Exakat\Exceptions\NoSuchProject;
use Exakat\Exceptions\ProjectNeeded;

class Remove extends Tasks {
    public const CONCURENCE = self::NONE;

    public function run(): void {
        $project = $this->config->project;

        if (!$project->validate()) {
            throw new InvalidProjectName($project->getError());
        }

        if ($this->config->project === 'default') {
            throw new ProjectNeeded();
        }

        if (!file_exists($this->config->projects_root . '/projects/' . $this->config->project)) {
            throw new NoSuchProject($this->config->project);
        }

        rmdirRecursive($this->config->projects_root . '/projects/' . $this->config->project);
        display('Project ' . $this->config->project . ' removed.');
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

use Exakat\Config as ConfigExakat;
use Exakat\Datastore;

class Jobqueue extends Tasks {
    public const CONCURENCE = self::QUEUE;
    public const PATH = '/tmp/queue.exakat';

    public const COMMANDS = array('quit', 'config', 'ping', 'project', 'onepage', 'report', 'init');

    private $pipefile = self::PATH;
    private $jobQueueLog = null;

    public function __destruct() {
        $this->log->log('Closed jobQueue');

        unlink($this->pipefile);
        fclose($this->jobQueueLog);

        parent::__destruct();
    }

    public function run(): void {
        if (!file_exists("{$this->config->projects_root}/projects/log/")) {
            mkdir("{$this->config->projects_root}/projects/log/", 0700);
        }
        $this->jobQueueLog = fopen("{$this->config->projects_root}/projects/log/jobqueue.log", 'a');
        $this->log('Open Job Queue ' . date('r') . "\n");

        $this->log->log('Started jobQueue : ' . time() . "\n");

        $queue = array();

        if (!file_exists($this->config->projects_root . '/projects/onepage')) {
            mkdir($this->config->projects_root . '/projects/onepage/', 0755);
            mkdir($this->config->projects_root . '/projects/onepage/code', 0755);
            mkdir($this->config->projects_root . '/projects/onepage/log', 0755);
        }
        if (!file_exists($this->config->projects_root . '/projects/onepage/reports')) {
            mkdir($this->config->projects_root . '/projects/onepage/reports', 0755);
        }

        //////// setup our named pipe ////////
        // @todo put this in config
        print "Opening $this->pipefile\n";
        if(file_exists($this->pipefile)) {
            if(!unlink($this->pipefile)) {
                die('unable to remove existing PipeFile "' . $this->pipefile . '". Aborting.' . "\n");
            }
        }

        umask(0);
        if(!posix_mkfifo($this->pipefile,0666)) {
            die('unable to create named pipe');
        }

        $pipe = fopen($this->pipefile,'r+');
        if(!$pipe) {
            die('unable to open the named pipe');
        }
        stream_set_blocking($pipe, false);

        //////// process the queue ////////
        while(1) {
            while($input = trim((string) fgets($pipe))) {
                stream_set_blocking($pipe, false);
                $queue[] = $input;
            }

            $job = current($queue);
            $jobkey = key($queue);

            if(empty($job)) {
                display( "no jobs to do - waiting...\n");
                stream_set_blocking($pipe, true);
            } else {
                $command = json_decode(trim($job));

                if ($command === null) {
                    $this->log('Unknown command : ' . $job . "\t" . time() . "\n");
                    next($queue);
                    unset($job, $queue[$jobkey]);
                    continue;
                }

                $command = array_merge(array('exakat'), $command);
                switch($command[1]) {
                    case 'init' :
                        $this->processInit($command);
                        break;

                    case 'project' :
                        $this->processProject($command);
                        break;

                    case 'report' :
                        $this->processReport($command);
                        break;

                    case 'remove' :
                        $this->processRemove($command);
                        break;

                    case 'config' :
                        $this->processConfig($command);
                        break;

                    default :
                        echo 'Unknown command "', $command[1], '"', PHP_EOL;
                        $this->log("Unknown command '$command[1]'");
                }

                next($queue);
                unset($job, $queue[$jobkey]);
            }
        }
    }

    private function processQuit($job) {
        display( "Received quit command. Bye\n");
        $this->log('Quit command');
        $this->log->log('Quit jobQueue : ' . time() . "\n");
        die();
    }

    private function processInit($job) {
        $config = new ConfigExakat($job);
        $analyze = new Initproject(self::IS_SUBTASK);

        display( 'processing init job ' . $job[2] . PHP_EOL);
        $this->log('start init : ' . $job[2]);
        $begin = microtime(true);
        try {
            $analyze->run();
        } catch (\Exception $e) {
            $datastore = new Datastore();
            $datastore->addRow('hash', array('init error' => $e->getMessage() ));
        } finally {
            unset($analyze);
        }
        $end = microtime(true);
        $this->log('end init : ' . $job[2] . ' (' . number_format($end -$begin, 2) . ' s)');
        display( 'processing init job ' . $job[2] . ' done (' . number_format($end -$begin, 2) . ' s)' . PHP_EOL);
    }

    private function processPing($job) {
        print 'pong' . PHP_EOL;
    }

    private function processReport($job) {
        $config = new ConfigExakat($job);
        if (!file_exists("{$this->config->projects_root}/projects/{$config->project}")) {
            $this->log("No such project as {$config->project}. Ignoring\n");
            return;
        }
        $analyze = new Report(self::IS_SUBTASK);

        display( 'processing report job ' . $job[2] . PHP_EOL);
        $this->log('start report : ' . $job[2]);
        $begin = microtime(true);
        $analyze->run();
        $end = microtime(true);
        unset($analyze);
        display( 'processing report job ' . $job[2] . ' done (' . number_format($end -$begin, 2) . ' s)' . PHP_EOL);
    }

    private function processProject($job) {
        $config = new ConfigExakat($job);
        if (!file_exists("{$this->config->projects_root}/projects/{$config->project}")) {
            $this->log("No such project as {$config->project}. Ignoring\n");
            return;
        }
        $analyze = new Project(self::IS_SUBTASK);

        display( 'processing project job ' . $job[2] . PHP_EOL);
        $this->log('start project : ' . $job[2]);
        $begin = microtime(true);
        try {
            $analyze->run();
        } catch (\Exception $e) {
            $datastore = new Datastore();
            $datastore->addRow('hash', array('init error' => $e->getMessage() ));
        } finally {
            unset($analyze);
        }
        $end = microtime(true);
        $this->log('end project : ' . $job[2] . ' (' . number_format($end -$begin, 2) . ' s)');
        display( 'processing project job ' . $job[2] . ' done (' . number_format($end -$begin, 2) . ' s)' . PHP_EOL);
    }

    private function processConfig($job) {
        $config = new ConfigExakat($job);
        if (!file_exists("{$this->config->projects_root}/projects/{$config->project}")) {
            $this->log("No such project as {$config->project}. Ignoring\n");
            return;
        }
        $analyze = new Config(self::IS_SUBTASK);

        display( 'processing config job ' . $job[2] . PHP_EOL);
        $this->log('start config : ' . $job[2]);
        $begin = microtime(true);
        try {
            $analyze->run();
        } catch (\Exception $e) {
        }
        $end = microtime(true);
        $this->log('end config : ' . $job[2] . ' (' . number_format($end -$begin, 2) . ' s)');
        unset($analyze);
        display( 'processing config job ' . $job[2] . ' done (' . number_format($end -$begin, 2) . ' s)' . PHP_EOL);
    }

    private function processRemove($job) {
        $config = new ConfigExakat($job);
        if (!file_exists("{$this->config->projects_root}/projects/{$config->project}")) {
            $this->log("No such project as {$config->project}. Ignoring\n");
            return;
        }
        $analyze = new Remove(self::IS_SUBTASK);

        display( 'processing remove job ' . $job[2] . PHP_EOL);
        $this->log('start report : ' . $job[2]);
        $begin = microtime(true);
        try {
            $analyze->run();
        } catch (\Exception $e) {
            $datastore = new Datastore();
            $datastore->addRow('hash', array('init error' => $e->getMessage() ));
        }
        $end = microtime(true);
        unset($analyze);
        display( 'processing remove job ' . $job[2] . ' done (' . number_format($end -$begin, 2) . ' s)' . PHP_EOL);
    }

    private function log($message) {
        fwrite($this->jobQueueLog, date('r') . "\t{$message}\n");
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks;

class Api extends Tasks {
    public const CONCURENCE = self::ANYTIME;

    public const PORT = 8447;

    public function run(): void {
        if ($this->config->stop    === true ||
            $this->config->restart === true
            ) {
            $display = @file_get_contents('http://localhost:' . self::PORT . '/?json=["stop"]');
            if (empty($display)) {
                $display = 'No server found';
            }
            display('Shut down server (' . $display . ')');

            if ($this->config->stop === true) {
                return;
            }
        }

        if (file_exists($this->config->dir_root . '/projects/api.php')) {
            display('An API is already installed. Aborting.');
            return;
        }

        display('Copy router server');
        $php = file_get_contents($this->config->dir_root . '/server/api.php');

        $tags = array('__PHP__', '__EXAKAT__', '__SECRET_KEY__');
        $values = array($this->config->php, $this->config->executable, $this->config->transit_key);
        $php = str_replace($tags, $values, $php);

        file_put_contents("{$this->config->projects_root}/projects/api.php", $php);

        if (!file_exists("{$this->config->projects_root}/projects/api.log")) {
            file_put_contents("{$this->config->projects_root}/projects/api.log", date('r') . "\tCreated file\n");
        }

        display('Start api');
        exec($this->config->php . ' -S 0.0.0.0:' . self::PORT . ' -t ' . $this->config->projects_root . '/projects/ ' . $this->config->projects_root . '/projects/api.php > /dev/null 2 > /dev/null &');
        display('Started api');
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class IsInIgnoredDir extends LoadFinal {
    public function run(): void {
        $ignoredfunctions = $this->datastore->getCol('ignoredfunctions', 'fullnspath');

        $countF = 0;
        if (!empty($ignoredfunctions)) {
            $query = $this->newQuery('IsInIgnoredDir functions');
            $query->atomIs('Functioncall', Analyzer::WITHOUT_CONSTANTS)
                  ->fullnspathIs($ignoredfunctions, Analyzer::CASE_SENSITIVE)
                  ->property('ignored_dir', true)
                  ->returnCount();
            $query->prepareRawQuery();
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $countF = $result->toInt();
        }

        $ignoredconstants = $this->datastore->getCol('ignoredconstants', 'fullnspath');
        $ignoredcit       = $this->datastore->getCol('ignoredcit', 'fullnspath');
        $ignored          = array_values(array_merge($ignoredcit, $ignoredconstants));

        $countC = 0;
        if (!empty($ignoredfunctions)) {
            $query = $this->newQuery('IsInIgnoredDir constants + cit');
            $query->atomIs(array('Identifier', 'Nsname'), Analyzer::WITHOUT_CONSTANTS)
                  ->fullnspathIs($ignored, Analyzer::CASE_SENSITIVE)
                  ->property('ignored_dir', true)
                  ->returnCount();
            $query->prepareRawQuery();
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $countC = $result->toInt();
        }

        $count = $countF + $countC;
        display("Set $count functions, constants and class with ignored_dir");
    }
}

?>
   Bud1            %                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   E   %                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       DSDB                             `                                                     @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              <?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class SpotPHPNativeConstants extends LoadFinal {
    public function run(): void {
        if (empty($this->PHPconstants)) {
            return;
        }
        $constants = array_merge(...$this->PHPconstants);
        $constants = array_filter($constants, function (string $x): bool { return strpos($x, '\\') === false;});
        $PHPconstants = array_values($constants);
        $PHPconstants = makeFullNsPath($PHPconstants, \FNP_CONSTANT);

        // Constants like A\E_ALL are ignored here

        // Constants like \E_ALL
        $query = $this->newQuery('SpotPHPNativeConstants nsname');
        $query->atomIs('Nsname', Analyzer::WITHOUT_CONSTANTS)
              ->is('absolute', true)
              ->has('fullnspath')
              ->hasNoIn('DEFINITION')
              ->fullnspathIs($PHPconstants, Analyzer::CASE_SENSITIVE)
              ->property('isPhp', true)
              ->returnCount();
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        display($result->toInt() . ' SpotPHPNativeConstants like \E_ALL');

        // constants like E_ALL
        $query = $this->newQuery('SpotPHPNativeConstants');
        $query->atomIs('Identifier', Analyzer::WITHOUT_CONSTANTS)
              ->tokenIs('T_STRING') // This will skip strings in define()
              ->has('fullnspath')
              ->values('fullnspath')
              ->unique();
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            $usedConstants = array();
        } else {
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $usedConstants = $result->toArray();
        }

        if (empty($usedConstants)) {
            display('No Constants');
            return;
        }

        $found = array();
        foreach($usedConstants as $constant) {
            if (!preg_match('/(\\\\[^\\\\]+)$/', $constant, $r)) {
                continue;
            }
            array_collect_by($found, $r[1], $constant);
        }

        $used = array_intersect(array_keys($found), $PHPconstants);
        if (empty($used)) {
            display('No PHP Constants');
            return;
        }

        $search = array();
        foreach($used as $key) {
            $search[] = $found[$key];
        }
        $search = array_merge(...$search);

        $query = $this->newQuery('SpotPHPNativeConstants');
        $query->atomIs('Identifier', Analyzer::WITHOUT_CONSTANTS)
              ->has('fullnspath')
              ->hasNoIn('DEFINITION')
              ->fullnspathIs($search, Analyzer::CASE_SENSITIVE)
              ->raw(<<<'GREMLIN'
sideEffect{ fnp = it.get().value("fullnspath").tokenize("\\").last();  
                it.get().property("fullnspath", "\\" + fnp);
                it.get().property("isPhp", true);}
GREMLIN
)
              ->returnCount();
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        display($result->toInt() . ' SpotPHPNativeConstants');

        $query = $this->newQuery('SpotPHPNativeConstants in Usenamespace');
        $query->atomIs('Usenamespace', Analyzer::WITHOUT_CONSTANTS)
              ->hasOut('CONST')
              ->outIs('USE')
              ->fullnspathIs($search, Analyzer::CASE_SENSITIVE)
              ->raw(<<<'GREMLIN'
sideEffect{ 
    fnp = it.get().value("fullnspath").tokenize("\\").last();  
    it.get().property("fullnspath", "\\"  + fnp);
    it.get().property("isPhp", true);
}
GREMLIN
)
              // propagate to all usage of this constant
              ->outIs('DEFINITION')
              ->raw(<<<'GREMLIN'
sideEffect{     
    it.get().property("fullnspath", "\\"  + fnp);
    it.get().property("isPhp", true);}
GREMLIN
)
              ->returnCount();
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        display($result->toInt() . ' SpotPHPNativeConstants in Use');
    }

    public function setPHPconstants(array $PHPconstants = array()): void {
        $this->PHPconstants = $PHPconstants;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class FinishExtends extends LoadFinal {

    public function run(): void {

        $query = $this->newQuery('Extends to all parents');
        $query->atomIs(array('Class', 'Classanonymous', 'Interface'), Analyzer::WITHOUT_CONSTANTS)
            // Reach first parent
              ->outIs('EXTENDS')
              ->inIs('DEFINITION')
              ->raw(<<<'GREMLIN'
as("gotoallparents").emit( )
.repeat( __.out("EXTENDS")
           .in("DEFINITION")
           .hasLabel("Class", "Classanonymous", "Interface", "Trait")
           .simplePath().from("gotoallparents")
        )
        .times(5)
        .hasLabel("Class", "Classanonymous", "Interface", "Trait")
GREMLIN
)
              ->outIs('EXTENDS')
              ->not(
                $query->side()
                      ->inIs('EXTENDS')
                      ->raw('where(eq("first"))')
              )
              ->addEFrom('EXTENDS', 'first', array('extra' => 'true'))
              ->returnCount();
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        $countExtends = $result->toInt();

        $query = $this->newQuery('Implements to all parents');
        $query->atomIs(array('Class', 'Classanonymous'), Analyzer::WITHOUT_CONSTANTS)
            // Reach first parent
              ->outIs(array('IMPLEMENTS', 'EXTENDS'))
              ->inIs('DEFINITION')
              ->raw(<<<'GREMLIN'
as("gotoallparents").emit( )
.repeat( __.out("EXTENDS", "IMPLEMENTS")
           .in("DEFINITION")
           .hasLabel("Class", "Classanonymous", "Interface")
           .simplePath().from("gotoallparents")
        )
        .times(5)
        .hasLabel("Class", "Classanonymous", "Interface")
        .dedup()
GREMLIN
)
              ->outIs(array('IMPLEMENTS', 'EXTENDS'))
              ->not(
                $query->side()
                      ->inIs('IMPLEMENTS')
                      ->raw('where(eq("first"))')
              )
              ->addEFrom('IMPLEMENTS', 'first', array('extra' => 'true'))
              ->returnCount();
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        $countImplements = $result->toInt();

        $query = $this->newQuery('Use trait to all parents');
        $query->atomIs(array('Class', 'Classanonymous', 'Trait'), Analyzer::WITHOUT_CONSTANTS)
            // find all class first
              ->raw(<<<'GREMLIN'
optional(
    __.as("gotoallparentsclass").emit( )
.repeat( __.out("EXTENDS")
           .in("DEFINITION")
           .hasLabel("Class")
           .simplePath().from("gotoallparentsclass")
        )
        .times(5)
        .hasLabel("Class", "Trait")
    )
GREMLIN
)
              ->raw(<<<'GREMLIN'
as("gotoallparents").emit( )
.repeat( __.out("USE").out("USE")
           .in("DEFINITION")
           .hasLabel("Trait")
           .simplePath().from("gotoallparents")
        )
        .times(5)
        .hasLabel("Trait", "Class")
GREMLIN
)
              ->outIs('USE')
              ->not(
                $query->side()
                      ->inIs('USE')
                      ->raw('where(eq("first"))')
              )
              ->addEFrom('USE', 'first', array('extra' => 'true'))
              ->returnCount();
        $query->prepareRawQuery();
        $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
        $countUse = $result->toInt();

        $count = $countExtends + $countImplements + $countUse;
        display("Created $count extends to parents");
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class FixFullnspathConstants extends LoadFinal {
    public function run(): void {
        $query = $this->newQuery('fixFullnspathConstants');
        $query->atomIs(array('Identifier', 'Nsname'), Analyzer::WITHOUT_CONSTANTS)
              ->has('fullnspath')
              ->_as('identifier')
              ->savePropertyAs('fullnspath', 'cc')
              ->inIs('DEFINITION')
              ->atomIs(array('Class', 'Trait', 'Interface', 'Constant', 'Defineconstant'), Analyzer::WITHOUT_CONSTANTS)
              ->raw(<<<'GREMLIN'
coalesce( __.out("ARGUMENT").has("rank", 0), 
          __.hasLabel("Constant").out('NAME'), 
          filter{ true; })
GREMLIN
)
              ->savePropertyAs('fullnspath', 'actual')
              ->raw('filter{ actual != cc; }')
              ->back('identifier')
              ->setProperty('fullnspath', 'actual')
              ->returnCount();
        $query->prepareRawQuery();
        if (!$query->canSkip()) {
            $this->gremlin->query($query->getQuery(), $query->getArguments());
        }

        display('Fixed Fullnspath for Constants');
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class FinishIsModified extends LoadFinal {
    protected $methods = null;

    public function __construct() {
        parent::__construct();

        $this->methods = exakat('methods');
    }

    public function run(): void {
        $variables = array('Variable',
                           'Variableobject',
                           'Variablearray',
                           'Array',
                           'Member',
                           'Staticproperty',
                           'Phpvariable',
                          );

        // No support for old style constructors
        $query = $this->newQuery('isModified with New');
        $query->atomIs('New', Analyzer::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->outIs('ARGUMENT')
              ->is('reference', true)
              ->savePropertyAs('rank', 'r')
              ->back('first')
              ->outIs('NEW')
              ->outIs('ARGUMENT')
              ->samePropertyAs('rank', 'r', Analyzer::CASE_SENSITIVE)
              ->atomIs($variables, Analyzer::WITHOUT_CONSTANTS)
              ->setProperty('isModified', true)
              ->returnCount();
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            $countNew = 0;
        } else {
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $countNew = $result->toInt();
        }

        $query = $this->newQuery('isModified with function calls');
        $query->atomIs(array('Functioncall', 'Methodcall', 'Staticmethodcall'), Analyzer::WITHOUT_CONSTANTS)
              ->inIs('DEFINITION')
              ->outIs('ARGUMENT')
              ->is('reference', true)
              ->savePropertyAs('rank', 'r')
              ->back('first')
              ->outIsIE('METHOD')
              ->outIs('ARGUMENT')
              ->samePropertyAs('rank', 'r', Analyzer::CASE_INSENSITIVE)
              ->atomIs($variables, Analyzer::WITHOUT_CONSTANTS)
              ->setProperty('isModified', true)
              ->returnCount();
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            $countFunction = 0;
        } else {
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $countFunction = $result->toInt();
        }

        $count = $countNew + $countFunction;
        display("Created $count isModified values");

        $query = $this->newQuery('isModified with list() or foreach()');
        $query->atomIs('Keyvalue', Analyzer::WITHOUT_CONSTANTS)
              ->inIs(array('VALUE', 'ARGUMENT'))
              ->atomIs(array('Foreach', 'List'), Analyzer::WITHOUT_CONSTANTS)
              ->back('first')
              ->outIs(array('INDEX', 'VALUE'))
              ->atomIs($variables, Analyzer::WITHOUT_CONSTANTS)
              ->setProperty('isModified', true)
              ->returnCount();
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            $countOperator = 0;
        } else {
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $countOperator = $result->toInt();
        }

        $count = $countFunction + $countOperator;
        display("Created $count isModified values with => ");
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class SpotPHPNativeFunctions extends LoadFinal {
    public function run(): void {
        $count = 0;

        $query = $this->newQuery('SpotPHPNativeFunctions fallingback');
        $query->atomIs('Functioncall', Analyzer::WITHOUT_CONSTANTS)
              ->isNot('absolute', true)
              ->tokenIs('T_STRING')
              ->has('fullnspath')
              ->hasNoIn('DEFINITION')
              ->not(
                $query->side()
                      ->outIs('NAME')
                      ->inIs('DEFINITION')
                      ->inIs('USE')
                      ->atomIs('Usenamespace', Analyzer::WITHOUT_CONSTANTS)
              )
              ->raw('map{ parts = it.get().value("fullnspath").tokenize("\\\\"); name = parts.last().toLowerCase();}')
              ->unique();
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            $fallingback = array();
        } else {
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $fallingback = $result->toArray();
        }

        if (!empty($fallingback)) {
            $phpfunctions = array_merge(...$this->PHPfunctions);
            $phpfunctions = array_map('strtolower', $phpfunctions);
            $phpfunctions = array_values($phpfunctions);

            $diff = array_values(array_intersect($fallingback, $phpfunctions));

            $query = $this->newQuery('SpotPHPNativeFunctions update');
            $query->atomIs('Functioncall', Analyzer::WITHOUT_CONSTANTS)
                  ->has('fullnspath')
                  ->isNot('absolute', true)
                  ->tokenIs('T_STRING')
                  ->hasNoIn('DEFINITION')
                  ->not(
                    $query->side()
                         ->outIs('NAME')
                         ->inIs('DEFINITION')
                         ->inIs('USE')
                         ->atomIs('Usenamespace', Analyzer::WITHOUT_CONSTANTS)
                  )
                  ->raw('filter{ name = it.get().value("fullnspath").tokenize("\\\\").last().toLowerCase(); name in *** }', $diff)
                  ->raw('sideEffect{
         fullnspath = "\\\\" + name;
         it.get().property("fullnspath", fullnspath);
         it.get().property("isPhp", true); 
     }')
                  ->returnCount();
            $query->prepareRawQuery();
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $count = $result->toInt();
        }

        display("Set $count functioncall fallingback");
    }

    public function setPHPfunctions(array $phpfunctions): void {
        $this->PHPfunctions = $phpfunctions;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2019 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Analyzer\Analyzer;

class SpotExtensionNativeFunctions extends LoadFinal {
    public function run(): void {
        $query = $this->newQuery('SpotExtensionNativeFunctions fallingback');
        $query->atomIs('Functioncall', Analyzer::WITHOUT_CONSTANTS)
              ->isNot('absolute', true)
              ->tokenIs('T_STRING')
              ->has('fullnspath')
              ->hasNoIn('DEFINITION')
              ->raw('filter{ parts = it.get().value("fullnspath").tokenize("\\\\"); parts.size() > 1 }')
              ->raw('map{ name = "\\\\" + parts.last().toLowerCase();}')
              ->unique();
        $query->prepareRawQuery();
        if ($query->canSkip()) {
            $fallingback = array();
        } else {
            $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
            $fallingback = $result->toArray();
        }

        if (empty($fallingback)) {
            display('Set 0 extension functioncall fallingback');
            return;
        }

        $functionsDev = $this->config->dev->loadIni('functions.ini', 'functions');
        $functionsExt = $this->config->ext->loadIni('functions.ini', 'functions');
        $functionsAll = array_unique(array_merge($functionsDev, $functionsExt));
        $diff = array_values(array_intersect($fallingback, $functionsAll));

        $query = $this->newQuery('SpotExtensionNativeFunctions update');
        $query->atomIs('Functioncall', Analyzer::WITHOUT_CONSTANTS)
              ->has('fullnspath')
              ->isNot('absolute', true)
              ->tokenIs('T_STRING')
              ->hasNoIn('DEFINITION')
              ->raw('filter{ parts = it.get().value("fullnspath").tokenize("\\\\"); parts.size() > 1 }')
              ->raw('filter{ name = "\\\\" + parts.last().toLowerCase(); name in *** }', $diff)
              ->raw('sideEffect{
         it.get().property("fullnspath", name); 
     }')
                  ->returnCount();
         $query->prepareRawQuery();
         $result = $this->gremlin->query($query->getQuery(), $query->getArguments());
         $count = $result->toInt();

        display("Set $count extension functioncall fallingback");
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\LoadFinal;

use Exakat\Query\Query;
use Exakat\Exceptions\GremlinException;
use Exakat\Log;
use Exakat\Helpers\Timer;

class LoadFinal {
    protected $gremlin    = null;
    protected $config     = null;
    protected $datastore  = null;

    protected $PHPconstants = array();
    protected $PHPfunctions = array();

    protected $log        = null;
    protected Timer $timer;

    public function __construct() {
        $this->gremlin    = exakat('graphdb');
        $this->config     = exakat('config');
        $this->datastore  = exakat('datastore');
        $this->datastore->reuse();

        $this->log = new Log(strtolower(substr(static::class, strrpos(self::class, '\\') + 1)),
                             "{$this->config->projects_root}/projects/{$this->config->project}");
        $this->timer = new Timer();
    }

    protected function newQuery(string $title): Query {
        return new Query(0,
                         $this->config->project,
                         $title,
                         $this->config->executable
                         );
    }

    public function run(): void {
        $this->log('Start');
        display('Start load final');

        $this->init();

        $this->removeInterfaceToClassExtends();
        $this->log('removeInterfaceToClassExtends');

        $this->fixFullnspathFunctions();
        $this->log('fixFullnspathFunctions');

        // stats calculation : it will fill the functioncall list
        $query = <<<'GREMLIN'
g.V().hasLabel("Functioncall")
     .has("fullnspath")
     .groupCount("m")
     .by("fullnspath")
     .cap("m")
GREMLIN;
        $fixed = $this->gremlin->query($query)->toArray();
        if (!empty($fixed)) {
            $this->datastore->addRow('functioncalls', $fixed[0]);
        }

        // This is needed AFTER functionnames are found
        $this->spotFallbackConstants();
        $this->log('spotFallbackConstants');

        $list = array('\Exakat\Tasks\LoadFinal\FixFullnspathConstants',
                      '\Exakat\Tasks\LoadFinal\FinishIsModified',
                      '\Exakat\Tasks\LoadFinal\IsInIgnoredDir',
                      '\Exakat\Tasks\LoadFinal\FinishExtends',
                      );
        foreach($list as $class) {
            $task = new $class();
            $task->run();
            $this->log($class);
        }

        display('End load final');
        $this->logTime('Final');
    }

    private function log(string $step): void {
        $this->logTime($step);
        $this->log->log($step);
    }

    private function logTime(string $step): void {
        static $log;

        if ($log === null) {
            $log = fopen("{$this->config->log_dir}/loadfinal.timing.csv", 'w+');
            if ($log === false) {
                return;
            }
        }

        $this->timer->end();

        fwrite($log, $step . "\t" . $this->timer->duration() . "\n");
        $this->timer = new Timer();
    }

    private function removeInterfaceToClassExtends(): void {
        display('fixing Definitions for traits and interfaces');

        $query = <<<'GREMLIN'
g.V().hasLabel("Interface")
     .out("EXTENDS")
     .inE()
     .hasLabel("DEFINITION")
     .where(__.outV().hasLabel("Class", "Trait", "Classanonymous"))
     .drop()
     .count();
GREMLIN;
        $result = $this->gremlin->query($query);

        display($result->toInt() . ' removed interface extends link');
        $this->log->log(__METHOD__);

        $query = <<<'GREMLIN'
g.V().hasLabel("Class")
     .out("EXTENDS")
     .inE()
     .hasLabel("DEFINITION")
     .where(__.outV().hasLabel("Interface", "Trait"))
     .drop()
     .count();
GREMLIN;
        $result = $this->gremlin->query($query);

        display($result->toInt() . ' removed class extends link');
        $this->log->log(__METHOD__);

        $query = <<<'GREMLIN'
g.V().hasLabel("Class")
     .out("IMPLEMENTS")
     .inE()
     .hasLabel("DEFINITION")
     .where(__.outV().hasLabel("Class", "Trait", "Classanonymous"))
     .drop()
     .count();
GREMLIN;
        $result = $this->gremlin->query($query);

        display($result->toInt() . ' removed class implements link');
        $this->log->log(__METHOD__);

        $query = <<<'GREMLIN'
g.V().hasLabel("Usetrait")
     .out("USE")
     .inE()
     .hasLabel("DEFINITION")
     .where(__.outV().hasLabel("Class", "Interface", "Classanonymous"))
     .drop()
     .count();
GREMLIN;
        $result = $this->gremlin->query($query);

        display($result->toInt() . ' removed class implements link');
        $this->log->log(__METHOD__);
    }

    // Can't move this to Query, because atoms and functioncall dictionaries are still unloaded
    private function fixFullnspathFunctions(): void {
        display('fixing Fullnspath for Functions');

        // Fix fullnspath with definition local to namespace when there is a definition
        // namesapce x { function substr() ; substr(); }

        $query = <<<'GREMLIN'
g.V().hasLabel("Functioncall", "Identifier", "Nsname")
     .not(__.in("NAME").not(hasLabel("Functioncall")))
     .not(__.in("CLASS").hasLabel("Instanceof"))
     .not(has("absolute", true))
     .not(has("token", within('T_NAME_QUALIFIED', 'T_NS_SEPARATOR')))
     .has("fullnspath")
     .as("identifier")
     .sideEffect{ cc = it.get().value("fullnspath");}
     .or(
         __.where(__.in("DEFINITION")
                    .hasLabel("Function", "Constant")
                    .optional(
                        __.hasLabel("Constant").out("NAME")
                    )
                    .filter{ actual = it.get().value("fullnspath"); cc != actual;}
                 ),
         __.has("isPhp", true).not(__.where(__.in('DEFINITION'))).sideEffect{ actual = '\\' + it.get().value("fullnspath").tokenize('\\\\').last();},
         __.has("isExt", true).not(__.where(__.in('DEFINITION'))).sideEffect{ actual = '\\' + it.get().value("fullnspath").tokenize('\\\\').last();},
      )
     .select("identifier")
     .sideEffect{ 
        it.get().property("fullnspath", actual);
    }
     .count();
GREMLIN;
        $result = $this->gremlin->query($query);

        display($result->toInt() . ' fixed Fullnspath for Functions and Constants');
        $this->log->log(__METHOD__);
    }

    private function runQuery(string $query, string $title, array $args = array(), string $method = __METHOD__): void {
        display($title);

        $this->logTime($title);

        try {
            $this->gremlin->query($query, $args);
        } catch (GremlinException $e) {
            // This should be handled nicely!!!
        }

        display('   /' . $title);
        $this->logTime('end ' . $title);
        $this->log->log($method);
    }

    private function spotFallbackConstants(): void {
        $this->logTime('spotFallbackConstants');
        display("spotFallbackConstants\n");

        // Define-style constant definitions
        $query = <<<'GREMLIN'
g.V().hasLabel("Defineconstant")
     .out("NAME")
     .hasLabel("String").has("noDelimiter").not( has("noDelimiter", '') )
     .filter{ (it.get().value("noDelimiter") =~ "(\\\\\\\\)\$").getCount() == 0 }
     .has('fullnspath')
     .values('fullnspath').unique();
GREMLIN;
        $defineConstants = $this->gremlin->query($query)
                                         ->toArray();

        $query = <<<'GREMLIN'
g.V().hasLabel("Const")
     .not( where( __.in("CONST") ) )  // Not a class or an interface
     .out("CONST")
     .out("NAME")
     .filter{ (it.get().value("fullnspath") =~ "^\\\\\\\\[^\\\\\\\\]+\$").getCount() == 1 }
     .values('fullnspath').unique();

GREMLIN;
        $constConstants = $this->gremlin->query($query)
                                        ->toArray();

        $constants = array_merge($constConstants, $defineConstants);
        $this->logTime('constants : ' . count($constants));

        if (empty($constants)) {
            display('Link constant definitions : skipping.');
            return;
        }
        if (!empty($defineConstants)) {
            // This only works with define() and case sensitivity
            $query = <<<'GREMLIN'
g.V().hasLabel("Identifier", "Nsname")
     .not( where( __.in("NAME", "METHOD", "MEMBER", "EXTENDS", "IMPLEMENTS", "CONSTANT", "AS", "CLASS", "DEFINITION", "GROUPUSE") ) )
     .has("token", without("T_CONST", "T_FUNCTION"))
     .has('fullnspath')
     .sideEffect{name = it.get().value("fullnspath"); }
     .filter{ name in arg1 }
     .addE("DEFINITION")
     .from( 
        __.V().hasLabel("Defineconstant")
             .as("a").out("NAME").hasLabel("String")
             .has("fullnspath")
             .filter{ it.get().value("fullnspath") == name}.select("a")
      ).count();

GREMLIN;
            $this->gremlin->query($query, array('arg1' => $defineConstants));

            // Second round, with fallback to global constants
            // Based on define() definitions
            $this->logTime('constants define : ' . count($defineConstants));

            $query = <<<'GREMLIN'
g.V().hasLabel("Identifier", "Nsname")
     .not( where( __.in("NAME", "METHOD", "MEMBER", "EXTENDS", "IMPLEMENTS", "CONSTANT", "AS", "CLASS", "DEFINITION", "GROUPUSE") ) )
     .filter{ name = "\\\\" + it.get().value("fullcode"); name in arg1 }
     .has('fullnspath')
     .sideEffect{
        fullnspath = "\\\\" + it.get().value("code");
        it.get().property("fullnspath", fullnspath); 
     }
     .addE('DEFINITION')
     .from( 
        __.V().hasLabel("Defineconstant")
             .as("a").out("NAME").hasLabel("String").has('fullnspath')
             .filter{ it.get().value("fullnspath") == name}.select('a')
      ).count()

GREMLIN;
            $this->gremlin->query($query, array('arg1' => $defineConstants));
        }

        $this->logTime('constants const : ' . count($constConstants));
        if (!empty($constConstants)) {
            // Based on const definitions
            $query = <<<'GREMLIN'
g.V().hasLabel("Identifier", "Nsname")
     .not( where( __.in("NAME", "DEFINITION", "EXTENDS", "IMPLEMENTS") ) )
     .filter{ name = "\\\\" + it.get().value("fullcode"); 
              name in arg1; }
     .sideEffect{
         it.get().property("fullnspath", name); 
     }
     .addE('DEFINITION')
     .from( 
        __.V().hasLabel("Const")
             .not( where( __.in("CONST") ) ) 
             .out("CONST")
             .out("NAME")
             .filter{ (it.get().value("fullnspath") =~ "^\\\\\\\\[^\\\\\\\\]+\$").getCount() == 1 }
       )
       .count()

GREMLIN;
            $this->gremlin->query($query, array('arg1' => $constConstants));
        }

        // TODO : handle case-insensitive
        $this->logTime('Constant definitions');
        display('Link constant definitions');
        $this->log->log(__METHOD__);
    }

    private function init(): void {
        // fallback for PHP and ext, class, function, constant
        // update fullnspath with fallback for functions

        $themes = exakat('rulesets');

        $exts = $themes->listAllAnalyzer('Extensions');
        $exts[] = 'php_constants';
        $exts[] = 'php_functions';

        foreach($exts as $ext) {
            $inifile = str_replace('Extensions\Ext', '', $ext) . '.ini';
            $fullpath = "{$this->config->dir_root}/data/$inifile";

            if (file_exists($fullpath)) {
                $iniFile = parse_ini_file($fullpath);
            } else {
                $inifile = str_replace('Extensions\Ext', '', $ext) . '.json';
                $fullpath = "{$this->config->dir_root}/data/$inifile";
                if (!file_exists($fullpath)) {
                    continue;
                }

                $jsonFile = file_get_contents($fullpath);
                $iniFile = json_decode($jsonFile, \JSON_ASSOCIATIVE);
            }

            if (!empty($iniFile['constants'][0])) {
                $this->PHPconstants[] = $iniFile['constants'];
            }

            if (!empty($iniFile['functions'][0])) {
                $this->PHPfunctions[] = $iniFile['functions'];
            }
        }
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

use Exakat\Config;

class BaselineStash {
    public const STRATEGIES = array('none', 'always');

    // 'none', 'always', '<Name>'
    private $baseline_strategy = 'none';
    private $baseline_dir       = '';
    private $use               = 'none';

    public const NO_BASELINE          = '';

    public function __construct(Config $config) {
        $this->baseline_strategy = $config->baseline_set;
        $this->baseline_dir      = $config->project_dir . '/baseline';
        $this->use               = $config->baseline_use;

        if (!file_exists($this->baseline_dir)) {
            if (!mkdir($this->baseline_dir,0700)) {
                display('Could not create the baseline directory. No baseline will be saved.');
            }
        }
    }

    public function copyPrevious(string $previous, string $name = ''): void {
        if (!file_exists($previous)) {
            display("No previous audit found. Omitting baseline\n");

            return;
        }

        if (!empty($name) && !in_array($name, self::STRATEGIES)) {
            // overwrite
            if (!copy($previous, $this->baseline_dir . '/' . $name . '.sqlite')) {
                display('Could not save the baseline with the name ' . $name);
            }

            return;
        }

        if ($this->baseline_strategy === 'none') {
            // Nothing to do
            return;
        }

        if ($this->baseline_strategy === 'always') {
            $baselines = glob("{$this->baseline_dir}/dump-*.sqlite");
            if (empty($baselines)) {
                $last_id = 1;
            } else {
                usort($baselines, function (string $a, string $b) { return $b <=> $a;} ); // simplistic reverse sorting
                $last = $baselines[0];
                $last_id = preg_match('/dump-(\d+)-/', $last, $r) ? (int) $r[1] : 1;
            }

            // Create a new
            // md5 is here for uuid purpose.
            $sqliteFilePrevious = $this->baseline_dir . '/dump-' . ($last_id + 1) . '-' . substr(md5($this->baseline_dir . ($last_id + 1)), 0, 7) . '.sqlite';
            if (!copy($previous, $sqliteFilePrevious)) {
                display('Could not save the baseline with the name ' . $name);
            }

            return;
        }
    }

    public function removeBaseline(string $id): void {
        $id = basename($id);
        if (file_exists("{$this->baseline_dir}/$id.sqlite")) {
            display("Removing baseline '$id'\n");
            unlink("{$this->baseline_dir}/$id.sqlite");

            return;
        }

        $baselines = glob("{$this->baseline_dir}/dump-*-$id.sqlite");
        if (!empty($baselines) && count($baselines) === 1) {
            $baseline = basename($baselines[0], '.sqlite');
            display("Removing baseline '$baseline'\n");

            unlink($baselines[0]);

            return;
        }

        display("Could not find $id baseline\n");
    }

    public function getBaseline(): string {
        if ($this->baseline_strategy === 'none') {
            return self::NO_BASELINE;
        }

        if ($this->baseline_strategy === 'always') {
            $baselines = glob("{$this->baseline_dir}//dump-*-*.sqlite");
            if (empty($baselines)) {
                return self::NO_BASELINE;
            }

            // Get the last one
            sort($baselines);
            return array_pop($baselines);
        }

        // full name in use
        if (file_exists("{$this->baseline_dir}/{$this->baseline_strategy}.sqlite")) {
            return "{$this->baseline_dir}/{$this->baseline_strategy}.sqlite";
        }

        // dump-xxx-AAAAAAA.sqlite name
        if (file_exists("{$this->baseline_dir}/dump-\d+-{$this->baseline_strategy}.sqlite")) {
            return "{$this->baseline_dir}/{$this->baseline_strategy}.sqlite";
        }

        return self::NO_BASELINE;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class StubJson {
    private $json = null;
    private $file = null;

    public function __construct(string $path) {
        $this->json = json_decode(file_get_contents($path) ?? '');
        $this->file = basename($path);
    }

    public function getFile(): string {
        return $this->file;
    }

    public function getFunctions(): array {
        $return = array();

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->functions ?? array() as $name => $function) {
                $return[] = mb_strtolower($namespace . $name);
            }
        }

        return $return;
    }

    public function getClasses(): array {
        $return = array();

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->classes ?? array() as $name => $classe) {
                $return[] = mb_strtolower($namespace . $name);
            }
        }

        return $return;
    }

    public function getConstants(): array {
        $return = array();

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->constants ?? array() as $name => $constant) {
                // constant name is untouched (case insensitive)
                $return[] = mb_strtolower($namespace) . $name;
            }
        }

        return $return;
    }

    public function getInterfaces(): array {
        $return = array();

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->interfaces ?? array() as $name => $interface) {
                $return[] = mb_strtolower($namespace . $name);
            }
        }

        return $return;
    }

    public function getTraits(): array {
        $return = array();

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->traits ?? array() as $name => $trait) {
                $return[] = mb_strtolower($namespace . $name);
            }
        }

        return $return;
    }

    public function getClassMethods(): array {
        $return = array(array());

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->classes ?? array() as $name => $classe) {
                $return[mb_strtolower($namespace . $name)] = array_map('mb_strtolower', array_keys( (array) ($classe->methods ?? array()) ));
            }
        }

        return $return;
    }


    public function getClassProperties(): array {
        $return = array(array());

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->classes ?? array() as $name => $classe) {
                $return[mb_strtolower($namespace . $name)] = array_keys((array) $classe->properties ?? array());
            }
        }

        return $return;
    }

    public function getClassConstants(): array {
        $return = array(array());

        foreach((array) $this->json->versions as $namespace => $space) {
            foreach($space->classes ?? array() as $name => $classe) {
                $return[mb_strtolower($namespace . $name)] = array_keys((array) ($classe->constants ?? array()));
            }
        }

        return $return;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

use Exakat\Tasks\Load;

class Constant extends Plugin {
    public $name = 'constant';
    public $type = 'boolean';

    private $deterministFunctions = array();

    private $skipAtoms = array('Trait'          => 1,
                               'Class'          => 1,
                               'Classanonymous' => 1,
                               'Interface'      => 1,
                               'Enum'           => 1,
                             );

    public function __construct() {
        parent::__construct();

        $deterministFunctions = exakat('methods')->getDeterministFunctions();
        $this->deterministFunctions = array_map(function (string $x): string { return "\\$x";}, $deterministFunctions);
    }

    public function run(Atom $atom, array $extras = array()): void {
        if (isset($this->skipAtoms[$atom->atom])) {
            return;
        }

        foreach($extras as $extra) {
            if (is_array($extra)) { continue; }
            if ($extra->constant === null)  {
                $atom->constant = null;
                return ;
            }
        }

        switch ($atom->atom) {
            case 'Integer' :
            case 'Float' :
            case 'Boolean' :
            case 'Null' :
            case 'Void' :
            case 'Nsname' :
            case 'Identifier' :
            case 'Staticclass' :
            case 'Name' :
            case 'Self' :
            case 'Static' :
            case 'Parent' :
                $atom->constant = true;
                break;

            case 'Addition' :
            case 'Multiplication' :
            case 'Logical' :
            case 'Coalesce' :
            case 'Bitshift' :
            case 'Comparison' :
                $atom->constant = $extras['LEFT']->constant && $extras['RIGHT']->constant;
                break;

            case 'Heredoc' :
                if ($atom->heredoc !== true) { // it is a now doc
                    $atom->constant = true;
                    break;
                }
                $constants = array_column($extras, 'constant');
                $atom->constant = array_reduce($constants, function (bool $carry, bool $item): bool { return $carry && $item; }, true);
                break;

            case 'String' :
            case 'Arrayliteral' :
            case 'Concatenation' :
            case 'Argument' :
            case 'Classalias':
            case 'Sequence' :
                $constants = array_column($extras, 'constant');
                $atom->constant = array_reduce($constants, function (bool $carry, bool $item): bool { return $carry && $item; }, true);
                break;

            case 'Return' :
                $atom->constant = $extras['RETURN']->constant;
                break;

            case 'Staticconstant' :
                $atom->constant = $extras['CLASS']->constant;
                break;

            case 'Not' :
                $atom->constant = $extras['NOT']->constant;
                break;

            case 'Keyvalue' :
                $atom->constant = $extras['INDEX']->constant && $extras['VALUE']->constant;
                break;

            case 'Parenthesis' :
                $atom->constant = $extras['CODE']->constant ?? false;
                break;

            case 'Yield' :
                $atom->constant = $extras['YIELD']->constant;
                break;

            case 'Defineconstant' :
                if (empty($extras)) {
                    break;
                }
                $atom->constant = $extras['NAME']->constant && ($extras['VALUE']->constant ?? false);
                break;

            case 'Ternary' :
                $atom->constant = $extras['CONDITION']->constant &&
                                  $extras['THEN']->constant      &&
                                  $extras['ELSE']->constant;
                break;

            case 'Closure' :
                $atom->constant = true;
                break;

            case 'Assignation' :
                $atom->constant = $extras['RIGHT']->constant && $atom->code === '=';
                break;

            case 'Functioncall' :
                if (empty($atom->fullnspath)) {
                    $atom->constant  = Load::NOT_CONSTANT_EXPRESSION;
                } elseif (in_array($atom->fullnspath, $this->deterministFunctions)) {
                    if (isset($extras[0])) {
                        $constants = array_column($extras, 'constant');
                        $atom->constant = array_reduce($constants, function (bool $carry, bool $item): bool { return $carry && $item; }, true);
                    } else {
                        $atom->constant  = Load::CONSTANT_EXPRESSION;
                    }
                } else {
                    $atom->constant  = Load::NOT_CONSTANT_EXPRESSION;
                }
                break;

            default :
                $atom->constant = Load::NOT_CONSTANT_EXPRESSION;
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class AtomGroup {
    private int $atomCount = 0;

    public function __construct(int $init = 0) {
        $this->atomCount = $init;
    }

    public function factory(string $atom, int $line, string $ws = ''): Atom {
        return new Atom(++$this->atomCount, $atom, $line, $ws);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class IsModified extends Plugin {
    public $name = 'isModified';
    public $type = 'boolean';
    private $variables = array('Variable', 'Array', 'Member', 'Staticproperty', 'Phpvariable', );

    public function run(Atom $atom, array $extras): void {
        switch ($atom->atom) {
            case 'Assignation' :
                if (in_array($extras['LEFT']->atom, $this->variables)) {
                    $extras['LEFT']->isModified = true;
                }
                break;

            case 'Cast' :
                if ($atom->token === 'T_UNSET_CAST' &&
                    in_array($extras['CAST']->atom, $this->variables)) {
                    $extras['CAST']->isModified = true;
                }
                break;

            case 'Catch' :
                if (isset($extras['VARIABLE']) &&
                    in_array($extras['VARIABLE']->atom, $this->variables)) {
                    $extras['VARIABLE']->isModified = true;
                }
                break;

            case 'Foreach' :
                if (in_array($extras['VALUE']->atom, $this->variables)) {
                    $extras['VALUE']->isModified = true;
                }
                if (isset($extras['INDEX']) && in_array($extras['INDEX']->atom, $this->variables)) {
                    $extras['INDEX']->isModified = true;
                }
                if (!empty(array_filter($extras, function ($x) { return (int) $x->reference; }))) {
                    $extras['SOURCE']->isModified = true;
                }
                break;

            case 'List' :
            case 'Unset' :
                foreach($extras as $extra) {
                    if (in_array($extra->atom, $this->variables)) {
                        $extra->isModified = true;
                    }
                }
                break;

            case 'Parametername' :
                $atom->isModified = true;
                break;

            case 'Arrayappend' :
                if (in_array($extras['APPEND']->atom, $this->variables)) {
                    $extras['APPEND']->isModified = true;
                }
                $atom->isModified = true;
                break;

            case 'Preplusplus' :
                if (in_array($extras['PREPLUSPLUS']->atom, $this->variables)) {
                    $extras['PREPLUSPLUS']->isModified = true;
                }
                break;

            case 'Postplusplus' :
                if (in_array($extras['POSTPLUSPLUS']->atom, $this->variables)) {
                    $extras['POSTPLUSPLUS']->isModified = true;
                }
                break;

            default :
//                print $atom->atom.PHP_EOL;
        }
    }
}

?>
   Bud1            %                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 @                                              @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   E   %                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       DSDB                             `                                                     @                                                @                                                @                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              <?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Nullval extends Plugin {
    public const NO_VALUE = null;

    public $name = 'isNull';
    public $type = 'boolean';

    public function run(Atom $atom, array $extras): void {
        // Ignoring $extras['LEFT'] === null
        if ($atom->atom === 'Assignation') {
            if ($atom->code === '=') {
                $atom->isNull =  $extras['RIGHT']->isNull;
            }

            return;
        }

        switch ($atom->atom) {
            case 'Boolean' :
            case 'Integer' :
            case 'Float' :
            case 'String' :
            case 'Addition' :
            case 'Multiplication' :
            case 'Power' :
            case 'Arrayliteral' :
            case 'Not' :
            case 'Logical' :
            case 'Heredoc' :
            case 'Concatenation' :
            case 'Bitshift' :
            case 'Comparison' :
            case 'Staticclass' :
            case 'Sequence' :
            case 'Magicconstant' :
            case 'Identifier' :
                $atom->isNull = false;
                break;

            case 'Null' :
            case 'Void' :
                $atom->isNull = true;
                break;

            case 'Parenthesis' :
                $atom->isNull = $extras['CODE']->isNull ?? false;
                break;

            case 'Ternary' :
                if ($extras['CONDITION']->isNull) {
                    $atom->isNull = $extras['THEN']->isNull;
                } else {
                    $atom->isNull = $extras['ELSE']->isNull;
                }
                break;

            case 'Constant' :
                $atom->isNull = $extras['VALUE']->isNull;
                break;

            case 'Coalesce' :
                if ($extras['LEFT']->isNull) {
                    $atom->isNull = $extras['LEFT']->isNull;
                } else {
                    $atom->isNull = $extras['RIGHT']->isNull;
                }
                break;

        default :

        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Lock {
    private $path = null;

    public function __construct(string $path, string $name) {
        assert(strpos($name, '\\') === false, "Wrong slash for analysis name '$name'.");
        $b = array_reverse(explode('/', $name));
        assert(count($b) === 2, "Wrong lock build for '$name'.");

        $name = $b[1] . '-' . $b[0];
        $this->path = $path . '/' . $name;
    }

    public function check(): bool {
        $fp = @fopen($this->path, 'x');
        if ($fp === false) {
            $this->path = null;
            return false;
        }
        if (flock($fp, LOCK_EX | LOCK_NB)) {
            fwrite($fp, (string) getmypid());
            return true;
        } else {
            $this->path = null;
            return false;
        }
    }

    public function __destruct() {
        if (!empty($this->path) && file_exists($this->path)) {
            $pid = file_get_contents($this->path);

            assert($pid == getmypid(), "Alert : removing wrong pid for $this->path");

            unlink($this->path);
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class AnonymousNames {
    private int $anonymousNames = 0;

    public const A_CLASS          = 'class';
    public const A_FUNCTION       = 'function';
    public const A_ARROW_FUNCTION = 'arrowfunction';

    // No constructor

    public function getName(string $type): string {
        if (!in_array($type, array(self::A_CLASS, self::A_FUNCTION, self::A_ARROW_FUNCTION), \STRICT_COMPARISON)) {
            throw new LoadError('Classes, Functions and ArrowFunctions are the only anonymous');
        }

        ++$this->anonymousNames;
        return "$type@$this->anonymousNames";
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php72 extends Php {

    // PHP tokens
    public const T_REQUIRE_ONCE                  = 262;
    public const T_REQUIRE                       = 261;
    public const T_EVAL                          = 260;
    public const T_INCLUDE_ONCE                  = 259;
    public const T_INCLUDE                       = 258;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_YIELD                         = 267;
    public const T_DOUBLE_ARROW                  = 268;
    public const T_YIELD_FROM                    = 269;
    public const T_POW_EQUAL                     = 281;
    public const T_SR_EQUAL                      = 280;
    public const T_SL_EQUAL                      = 279;
    public const T_XOR_EQUAL                     = 278;
    public const T_OR_EQUAL                      = 277;
    public const T_AND_EQUAL                     = 276;
    public const T_MOD_EQUAL                     = 275;
    public const T_CONCAT_EQUAL                  = 274;
    public const T_DIV_EQUAL                     = 273;
    public const T_MUL_EQUAL                     = 272;
    public const T_MINUS_EQUAL                   = 271;
    public const T_PLUS_EQUAL                    = 270;
    public const T_COALESCE                      = 282;
    public const T_BOOLEAN_OR                    = 283;
    public const T_BOOLEAN_AND                   = 284;
    public const T_SPACESHIP                     = 289;
    public const T_IS_NOT_IDENTICAL              = 288;
    public const T_IS_IDENTICAL                  = 287;
    public const T_IS_NOT_EQUAL                  = 286;
    public const T_IS_EQUAL                      = 285;
    public const T_IS_GREATER_OR_EQUAL           = 291;
    public const T_IS_SMALLER_OR_EQUAL           = 290;
    public const T_SR                            = 293;
    public const T_SL                            = 292;
    public const T_INSTANCEOF                    = 294;
    public const T_UNSET_CAST                    = 303;
    public const T_BOOL_CAST                     = 302;
    public const T_OBJECT_CAST                   = 301;
    public const T_ARRAY_CAST                    = 300;
    public const T_STRING_CAST                   = 299;
    public const T_DOUBLE_CAST                   = 298;
    public const T_INT_CAST                      = 297;
    public const T_DEC                           = 296;
    public const T_INC                           = 295;
    public const T_POW                           = 304;
    public const T_CLONE                         = 306;
    public const T_NEW                           = 305;
    public const T_ELSEIF                        = 308;
    public const T_ELSE                          = 309;
    public const T_ENDIF                         = 310;
    public const T_PUBLIC                        = 316;
    public const T_PROTECTED                     = 315;
    public const T_PRIVATE                       = 314;
    public const T_FINAL                         = 313;
    public const T_ABSTRACT                      = 312;
    public const T_STATIC                        = 311;
    public const T_LNUMBER                       = 317;
    public const T_DNUMBER                       = 318;
    public const T_STRING                        = 319;
    public const T_VARIABLE                      = 320;
    public const T_INLINE_HTML                   = 321;
    public const T_ENCAPSED_AND_WHITESPACE       = 322;
    public const T_CONSTANT_ENCAPSED_STRING      = 323;
    public const T_STRING_VARNAME                = 324;
    public const T_NUM_STRING                    = 325;
    public const T_EXIT                          = 326;
    public const T_IF                            = 327;
    public const T_ECHO                          = 328;
    public const T_DO                            = 329;
    public const T_WHILE                         = 330;
    public const T_ENDWHILE                      = 331;
    public const T_FOR                           = 332;
    public const T_ENDFOR                        = 333;
    public const T_FOREACH                       = 334;
    public const T_ENDFOREACH                    = 335;
    public const T_DECLARE                       = 336;
    public const T_ENDDECLARE                    = 337;
    public const T_AS                            = 338;
    public const T_SWITCH                        = 339;
    public const T_ENDSWITCH                     = 340;
    public const T_CASE                          = 341;
    public const T_DEFAULT                       = 342;
    public const T_BREAK                         = 343;
    public const T_CONTINUE                      = 344;
    public const T_GOTO                          = 345;
    public const T_FUNCTION                      = 346;
    public const T_CONST                         = 347;
    public const T_RETURN                        = 348;
    public const T_TRY                           = 349;
    public const T_CATCH                         = 350;
    public const T_FINALLY                       = 351;
    public const T_THROW                         = 352;
    public const T_USE                           = 353;
    public const T_INSTEADOF                     = 354;
    public const T_GLOBAL                        = 355;
    public const T_VAR                           = 356;
    public const T_UNSET                         = 357;
    public const T_ISSET                         = 358;
    public const T_EMPTY                         = 359;
    public const T_HALT_COMPILER                 = 360;
    public const T_CLASS                         = 361;
    public const T_TRAIT                         = 362;
    public const T_INTERFACE                     = 363;
    public const T_EXTENDS                       = 364;
    public const T_IMPLEMENTS                    = 365;
    public const T_OBJECT_OPERATOR               = 366;
    public const T_LIST                          = 367;
    public const T_ARRAY                         = 368;
    public const T_CALLABLE                      = 369;
    public const T_LINE                          = 370;
    public const T_FILE                          = 371;
    public const T_DIR                           = 372;
    public const T_CLASS_C                       = 373;
    public const T_TRAIT_C                       = 374;
    public const T_METHOD_C                      = 375;
    public const T_FUNC_C                        = 376;
    public const T_COMMENT                       = 377;
    public const T_DOC_COMMENT                   = 378;
    public const T_OPEN_TAG                      = 379;
    public const T_OPEN_TAG_WITH_ECHO            = 380;
    public const T_CLOSE_TAG                     = 381;
    public const T_WHITESPACE                    = 382;
    public const T_START_HEREDOC                 = 383;
    public const T_END_HEREDOC                   = 384;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 385;
    public const T_CURLY_OPEN                    = 386;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 387;
    public const T_NAMESPACE                     = 388;
    public const T_NS_C                          = 389;
    public const T_NS_SEPARATOR                  = 390;
    public const T_ELLIPSIS                      = 391;
    public const T_DOUBLE_COLON                  = 387;
    public const T_COALESCE_EQUAL                = 1000;
    public const T_FN                            = 1000;
    public const T_BAD_CHARACTER                 = 1000;
    public const T_CHARACTER                     = 1000;
    public const T_NAME_FULLY_QUALIFIED          = 1000;
    public const T_NAME_RELATIVE                 = 1000;
    public const T_NAME_QUALIFIED                = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR      = 1000;
    public const T_MATCH                         = 1000;
    public const T_ATTRIBUTE                     = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

use stdClass;
use Exakat\Tasks\Load;

abstract class AtomInterface {
    public $id           = 0;
    public $atom         = 'No Atom Set';
    public $code         = '';
    public $lccode       = '';
    public $fullcode     = '';
    public $line         = Load::NO_LINE;
    public $token        = '';
    public $rank         = ''; // Not 0
    public $rankName     = '';
    public $alternative  = Load::NOT_ALTERNATIVE;
    public $reference    = Load::NOT_REFERENCE;
    public $heredoc      = false;
    public $delimiter    = null;
    public $noDelimiter  = null;
    public $variadic     = Load::NOT_VARIADIC;
    public $count        = null;
    public $fullnspath   = '';
    public $absolute     = Load::NOT_ABSOLUTE;
    public $alias        = '';
    public $origin       = '';
    public $encoding     = '';
    public $block        = '';
    public $intval       = Intval::NO_VALUE;
    public $strval       = Strval::NO_VALUE;
    public $boolean      = Boolval::NO_VALUE;
    public $enclosing    = Load::NO_ENCLOSING;
    public $args_max     = '';
    public $args_min     = '';
    public $bracket      = Load::NOT_BRACKET;
    public $flexible     = Load::NOT_FLEXIBLE;
    public $close_tag    = Load::NO_CLOSING_TAG;
    public $propertyname = '';
    public $constant     = Load::NOT_CONSTANT_EXPRESSION;
    public $globalvar    = false;
    public $binaryString = Load::NOT_BINARY;
    public $isNull       = false;
    public $visibility   = '';
    public $final        = '';
    public $abstract     = false;
    public $readonly     = false;
    public $static       = '';
    public $noscream     = 0;
    public $trailing     = 0;
    public $isRead       = 0;
    public $isModified   = 0;
    public $use          = '';
    public $typehint     = 'one';
    public $isPhp        = 0;
    public $isExt        = 0;
    public $isStub       = 0;
    public $position     = 0;
    public Whitespace $ws ;

    abstract public function toArray(): array;

    abstract public function toGraphsonLine(int &$id): stdClass;

    abstract public function boolProperties(): array;

    abstract public function isA(array $atoms): bool;
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php73 extends Php {

    // PHP tokens
    public const T_REQUIRE_ONCE                  = 258;
    public const T_REQUIRE                       = 259;
    public const T_EVAL                          = 260;
    public const T_INCLUDE_ONCE                  = 261;
    public const T_INCLUDE                       = 262;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_YIELD                         = 267;
    public const T_DOUBLE_ARROW                  = 268;
    public const T_YIELD_FROM                    = 269;
    public const T_POW_EQUAL                     = 270;
    public const T_SR_EQUAL                      = 271;
    public const T_SL_EQUAL                      = 272;
    public const T_XOR_EQUAL                     = 273;
    public const T_OR_EQUAL                      = 274;
    public const T_AND_EQUAL                     = 275;
    public const T_MOD_EQUAL                     = 276;
    public const T_CONCAT_EQUAL                  = 277;
    public const T_DIV_EQUAL                     = 278;
    public const T_MUL_EQUAL                     = 279;
    public const T_MINUS_EQUAL                   = 280;
    public const T_PLUS_EQUAL                    = 281;
    public const T_COALESCE                      = 282;
    public const T_BOOLEAN_OR                    = 283;
    public const T_BOOLEAN_AND                   = 284;
    public const T_SPACESHIP                     = 285;
    public const T_IS_NOT_IDENTICAL              = 286;
    public const T_IS_IDENTICAL                  = 287;
    public const T_IS_NOT_EQUAL                  = 288;
    public const T_IS_EQUAL                      = 289;
    public const T_IS_GREATER_OR_EQUAL           = 290;
    public const T_IS_SMALLER_OR_EQUAL           = 291;
    public const T_SR                            = 292;
    public const T_SL                            = 293;
    public const T_INSTANCEOF                    = 294;
    public const T_UNSET_CAST                    = 295;
    public const T_BOOL_CAST                     = 296;
    public const T_OBJECT_CAST                   = 297;
    public const T_ARRAY_CAST                    = 298;
    public const T_STRING_CAST                   = 299;
    public const T_DOUBLE_CAST                   = 300;
    public const T_INT_CAST                      = 301;
    public const T_DEC                           = 302;
    public const T_INC                           = 303;
    public const T_POW                           = 304;
    public const T_CLONE                         = 305;
    public const T_NEW                           = 306;
    public const T_ELSEIF                        = 308;
    public const T_ELSE                          = 309;
    public const T_ENDIF                         = 310;
    public const T_PUBLIC                        = 311;
    public const T_PROTECTED                     = 312;
    public const T_PRIVATE                       = 313;
    public const T_FINAL                         = 314;
    public const T_ABSTRACT                      = 315;
    public const T_STATIC                        = 316;
    public const T_LNUMBER                       = 317;
    public const T_DNUMBER                       = 318;
    public const T_STRING                        = 319;
    public const T_VARIABLE                      = 320;
    public const T_INLINE_HTML                   = 321;
    public const T_ENCAPSED_AND_WHITESPACE       = 322;
    public const T_CONSTANT_ENCAPSED_STRING      = 323;
    public const T_STRING_VARNAME                = 324;
    public const T_NUM_STRING                    = 325;
    public const T_EXIT                          = 326;
    public const T_IF                            = 327;
    public const T_ECHO                          = 328;
    public const T_DO                            = 329;
    public const T_WHILE                         = 330;
    public const T_ENDWHILE                      = 331;
    public const T_FOR                           = 332;
    public const T_ENDFOR                        = 333;
    public const T_FOREACH                       = 334;
    public const T_ENDFOREACH                    = 335;
    public const T_DECLARE                       = 336;
    public const T_ENDDECLARE                    = 337;
    public const T_AS                            = 338;
    public const T_SWITCH                        = 339;
    public const T_ENDSWITCH                     = 340;
    public const T_CASE                          = 341;
    public const T_DEFAULT                       = 342;
    public const T_BREAK                         = 343;
    public const T_CONTINUE                      = 344;
    public const T_GOTO                          = 345;
    public const T_FUNCTION                      = 346;
    public const T_CONST                         = 347;
    public const T_RETURN                        = 348;
    public const T_TRY                           = 349;
    public const T_CATCH                         = 350;
    public const T_FINALLY                       = 351;
    public const T_THROW                         = 352;
    public const T_USE                           = 353;
    public const T_INSTEADOF                     = 354;
    public const T_GLOBAL                        = 355;
    public const T_VAR                           = 356;
    public const T_UNSET                         = 357;
    public const T_ISSET                         = 358;
    public const T_EMPTY                         = 359;
    public const T_HALT_COMPILER                 = 360;
    public const T_CLASS                         = 361;
    public const T_TRAIT                         = 362;
    public const T_INTERFACE                     = 363;
    public const T_EXTENDS                       = 364;
    public const T_IMPLEMENTS                    = 365;
    public const T_OBJECT_OPERATOR               = 366;
    public const T_LIST                          = 367;
    public const T_ARRAY                         = 368;
    public const T_CALLABLE                      = 369;
    public const T_LINE                          = 370;
    public const T_FILE                          = 371;
    public const T_DIR                           = 372;
    public const T_CLASS_C                       = 373;
    public const T_TRAIT_C                       = 374;
    public const T_METHOD_C                      = 375;
    public const T_FUNC_C                        = 376;
    public const T_COMMENT                       = 377;
    public const T_DOC_COMMENT                   = 378;
    public const T_OPEN_TAG                      = 379;
    public const T_OPEN_TAG_WITH_ECHO            = 380;
    public const T_CLOSE_TAG                     = 381;
    public const T_WHITESPACE                    = 382;
    public const T_START_HEREDOC                 = 383;
    public const T_END_HEREDOC                   = 384;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 385;
    public const T_CURLY_OPEN                    = 386;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 387;
    public const T_NAMESPACE                     = 388;
    public const T_NS_C                          = 389;
    public const T_NS_SEPARATOR                  = 390;
    public const T_ELLIPSIS                      = 391;
    public const T_DOUBLE_COLON                  = 387;
    public const T_BAD_CHARACTER                 = 395;
    public const T_COALESCE_EQUAL                = 1000;
    public const T_FN                            = 1000;
    public const T_CHARACTER                     = 1000;
    public const T_NAME_FULLY_QUALIFIED          = 1000;
    public const T_NAME_RELATIVE                 = 1000;
    public const T_NAME_QUALIFIED                = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR      = 1000;
    public const T_MATCH                         = 1000;
    public const T_ATTRIBUTE                     = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php71 extends Php {

    // PHP tokens
    public const T_REQUIRE_ONCE                  = 262;
    public const T_REQUIRE                       = 261;
    public const T_EVAL                          = 260;
    public const T_INCLUDE_ONCE                  = 259;
    public const T_INCLUDE                       = 258;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_YIELD                         = 267;
    public const T_DOUBLE_ARROW                  = 268;
    public const T_YIELD_FROM                    = 269;
    public const T_POW_EQUAL                     = 281;
    public const T_SR_EQUAL                      = 280;
    public const T_SL_EQUAL                      = 279;
    public const T_XOR_EQUAL                     = 278;
    public const T_OR_EQUAL                      = 277;
    public const T_AND_EQUAL                     = 276;
    public const T_MOD_EQUAL                     = 275;
    public const T_CONCAT_EQUAL                  = 274;
    public const T_DIV_EQUAL                     = 273;
    public const T_MUL_EQUAL                     = 272;
    public const T_MINUS_EQUAL                   = 271;
    public const T_PLUS_EQUAL                    = 270;
    public const T_COALESCE                      = 282;
    public const T_BOOLEAN_OR                    = 283;
    public const T_BOOLEAN_AND                   = 284;
    public const T_SPACESHIP                     = 289;
    public const T_IS_NOT_IDENTICAL              = 288;
    public const T_IS_IDENTICAL                  = 287;
    public const T_IS_NOT_EQUAL                  = 286;
    public const T_IS_EQUAL                      = 285;
    public const T_IS_GREATER_OR_EQUAL           = 291;
    public const T_IS_SMALLER_OR_EQUAL           = 290;
    public const T_SR                            = 293;
    public const T_SL                            = 292;
    public const T_INSTANCEOF                    = 294;
    public const T_UNSET_CAST                    = 303;
    public const T_BOOL_CAST                     = 302;
    public const T_OBJECT_CAST                   = 301;
    public const T_ARRAY_CAST                    = 300;
    public const T_STRING_CAST                   = 299;
    public const T_DOUBLE_CAST                   = 298;
    public const T_INT_CAST                      = 297;
    public const T_DEC                           = 296;
    public const T_INC                           = 295;
    public const T_POW                           = 304;
    public const T_CLONE                         = 306;
    public const T_NEW                           = 305;
    public const T_ELSEIF                        = 308;
    public const T_ELSE                          = 309;
    public const T_ENDIF                         = 310;
    public const T_PUBLIC                        = 316;
    public const T_PROTECTED                     = 315;
    public const T_PRIVATE                       = 314;
    public const T_FINAL                         = 313;
    public const T_ABSTRACT                      = 312;
    public const T_STATIC                        = 311;
    public const T_LNUMBER                       = 317;
    public const T_DNUMBER                       = 318;
    public const T_STRING                        = 319;
    public const T_VARIABLE                      = 320;
    public const T_INLINE_HTML                   = 321;
    public const T_ENCAPSED_AND_WHITESPACE       = 322;
    public const T_CONSTANT_ENCAPSED_STRING      = 323;
    public const T_STRING_VARNAME                = 324;
    public const T_NUM_STRING                    = 325;
    public const T_EXIT                          = 326;
    public const T_IF                            = 327;
    public const T_ECHO                          = 328;
    public const T_DO                            = 329;
    public const T_WHILE                         = 330;
    public const T_ENDWHILE                      = 331;
    public const T_FOR                           = 332;
    public const T_ENDFOR                        = 333;
    public const T_FOREACH                       = 334;
    public const T_ENDFOREACH                    = 335;
    public const T_DECLARE                       = 336;
    public const T_ENDDECLARE                    = 337;
    public const T_AS                            = 338;
    public const T_SWITCH                        = 339;
    public const T_ENDSWITCH                     = 340;
    public const T_CASE                          = 341;
    public const T_DEFAULT                       = 342;
    public const T_BREAK                         = 343;
    public const T_CONTINUE                      = 344;
    public const T_GOTO                          = 345;
    public const T_FUNCTION                      = 346;
    public const T_CONST                         = 347;
    public const T_RETURN                        = 348;
    public const T_TRY                           = 349;
    public const T_CATCH                         = 350;
    public const T_FINALLY                       = 351;
    public const T_THROW                         = 352;
    public const T_USE                           = 353;
    public const T_INSTEADOF                     = 354;
    public const T_GLOBAL                        = 355;
    public const T_VAR                           = 356;
    public const T_UNSET                         = 357;
    public const T_ISSET                         = 358;
    public const T_EMPTY                         = 359;
    public const T_HALT_COMPILER                 = 360;
    public const T_CLASS                         = 361;
    public const T_TRAIT                         = 362;
    public const T_INTERFACE                     = 363;
    public const T_EXTENDS                       = 364;
    public const T_IMPLEMENTS                    = 365;
    public const T_OBJECT_OPERATOR               = 366;
    public const T_LIST                          = 367;
    public const T_ARRAY                         = 368;
    public const T_CALLABLE                      = 369;
    public const T_LINE                          = 370;
    public const T_FILE                          = 371;
    public const T_DIR                           = 372;
    public const T_CLASS_C                       = 373;
    public const T_TRAIT_C                       = 374;
    public const T_METHOD_C                      = 375;
    public const T_FUNC_C                        = 376;
    public const T_COMMENT                       = 377;
    public const T_DOC_COMMENT                   = 378;
    public const T_OPEN_TAG                      = 379;
    public const T_OPEN_TAG_WITH_ECHO            = 380;
    public const T_CLOSE_TAG                     = 381;
    public const T_WHITESPACE                    = 382;
    public const T_START_HEREDOC                 = 383;
    public const T_END_HEREDOC                   = 384;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 385;
    public const T_CURLY_OPEN                    = 386;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 387;
    public const T_NAMESPACE                     = 388;
    public const T_NS_C                          = 389;
    public const T_NS_SEPARATOR                  = 390;
    public const T_ELLIPSIS                      = 391;
    public const T_DOUBLE_COLON                  = 387;
    public const T_COALESCE_EQUAL                = 1000;
    public const T_FN                            = 1000;
    public const T_BAD_CHARACTER                 = 1000;
    public const T_CHARACTER                     = 1000;
    public const T_NAME_FULLY_QUALIFIED          = 1000;
    public const T_NAME_RELATIVE                 = 1000;
    public const T_NAME_QUALIFIED                = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR      = 1000;
    public const T_MATCH                         = 1000;
    public const T_ATTRIBUTE                     = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/



namespace Exakat\Tasks\Helpers;

abstract class Plugin {
    protected const NOT_PROVIDED = '';

    public function __construct() {}

    abstract public function run(Atom $atom, array $extras): void;
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class NestedCollector {
    public const THE_END = 1234;

    private $previous = array();
    private $current = array(self::THE_END);

    public function push(): void {
        $this->previous[] = $this->current;
        $this->current = array();
    }

    public function pop(): void {
        $this->current = array_pop($this->previous);
    }

    public function add($arg): void {
        $this->current[] = $arg;
    }

    public function getAll(): array {
        $return = $this->current;
        $this->current = array();

        return $return;
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class Php70 extends Php {

    // PHP tokens
    public const T_REQUIRE_ONCE                                     = 272;
    public const T_REQUIRE                                          = 271;
    public const T_EVAL                                             = 270;
    public const T_INCLUDE_ONCE                                     = 269;
    public const T_INCLUDE                                          = 268;
    public const T_LOGICAL_OR                                       = 273;
    public const T_LOGICAL_XOR                                      = 274;
    public const T_LOGICAL_AND                                      = 275;
    public const T_PRINT                                            = 276;
    public const T_YIELD                                            = 277;
    public const T_DOUBLE_ARROW                                     = 363;
    public const T_YIELD_FROM                                       = 278;
    public const T_POW_EQUAL                                        = 391;
    public const T_SR_EQUAL                                         = 289;
    public const T_SL_EQUAL                                         = 288;
    public const T_XOR_EQUAL                                        = 287;
    public const T_OR_EQUAL                                         = 286;
    public const T_AND_EQUAL                                        = 285;
    public const T_MOD_EQUAL                                        = 284;
    public const T_CONCAT_EQUAL                                     = 283;
    public const T_DIV_EQUAL                                        = 282;
    public const T_MUL_EQUAL                                        = 281;
    public const T_MINUS_EQUAL                                      = 280;
    public const T_PLUS_EQUAL                                       = 279;
    public const T_COALESCE                                         = 389;
    public const T_BOOLEAN_OR                                       = 290;
    public const T_BOOLEAN_AND                                      = 291;
    public const T_SPACESHIP                                        = 298;
    public const T_IS_NOT_IDENTICAL                                 = 295;
    public const T_IS_IDENTICAL                                     = 294;
    public const T_IS_NOT_EQUAL                                     = 293;
    public const T_IS_EQUAL                                         = 292;
    public const T_IS_GREATER_OR_EQUAL                              = 297;
    public const T_IS_SMALLER_OR_EQUAL                              = 296;
    public const T_SR                                               = 300;
    public const T_SL                                               = 299;
    public const T_INSTANCEOF                                       = 301;
    public const T_UNSET_CAST                                       = 310;
    public const T_BOOL_CAST                                        = 309;
    public const T_OBJECT_CAST                                      = 308;
    public const T_ARRAY_CAST                                       = 307;
    public const T_STRING_CAST                                      = 306;
    public const T_DOUBLE_CAST                                      = 305;
    public const T_INT_CAST                                         = 304;
    public const T_DEC                                              = 303;
    public const T_INC                                              = 302;
    public const T_POW                                              = 390;
    public const T_CLONE                                            = 312;
    public const T_NEW                                              = 311;
    public const T_ELSEIF                                           = 315;
    public const T_ELSE                                             = 316;
    public const T_ENDIF                                            = 317;
    public const T_PUBLIC                                           = 351;
    public const T_PROTECTED                                        = 350;
    public const T_PRIVATE                                          = 349;
    public const T_FINAL                                            = 348;
    public const T_ABSTRACT                                         = 347;
    public const T_STATIC                                           = 346;
    public const T_LNUMBER                                          = 259;
    public const T_DNUMBER                                          = 260;
    public const T_STRING                                           = 261;
    public const T_VARIABLE                                         = 262;
    public const T_INLINE_HTML                                      = 263;
    public const T_ENCAPSED_AND_WHITESPACE                          = 264;
    public const T_CONSTANT_ENCAPSED_STRING                         = 265;
    public const T_STRING_VARNAME                                   = 266;
    public const T_NUM_STRING                                       = 267;
    public const T_EXIT                                             = 313;
    public const T_IF                                               = 314;
    public const T_ECHO                                             = 318;
    public const T_DO                                               = 319;
    public const T_WHILE                                            = 320;
    public const T_ENDWHILE                                         = 321;
    public const T_FOR                                              = 322;
    public const T_ENDFOR                                           = 323;
    public const T_FOREACH                                          = 324;
    public const T_ENDFOREACH                                       = 325;
    public const T_DECLARE                                          = 326;
    public const T_ENDDECLARE                                       = 327;
    public const T_AS                                               = 328;
    public const T_SWITCH                                           = 329;
    public const T_ENDSWITCH                                        = 330;
    public const T_CASE                                             = 331;
    public const T_DEFAULT                                          = 332;
    public const T_BREAK                                            = 333;
    public const T_CONTINUE                                         = 334;
    public const T_GOTO                                             = 335;
    public const T_FUNCTION                                         = 336;
    public const T_CONST                                            = 337;
    public const T_RETURN                                           = 338;
    public const T_TRY                                              = 339;
    public const T_CATCH                                            = 340;
    public const T_FINALLY                                          = 341;
    public const T_THROW                                            = 342;
    public const T_USE                                              = 343;
    public const T_INSTEADOF                                        = 344;
    public const T_GLOBAL                                           = 345;
    public const T_VAR                                              = 352;
    public const T_UNSET                                            = 353;
    public const T_ISSET                                            = 354;
    public const T_EMPTY                                            = 355;
    public const T_HALT_COMPILER                                    = 356;
    public const T_CLASS                                            = 357;
    public const T_TRAIT                                            = 358;
    public const T_INTERFACE                                        = 359;
    public const T_EXTENDS                                          = 360;
    public const T_IMPLEMENTS                                       = 361;
    public const T_OBJECT_OPERATOR                                  = 362;
    public const T_LIST                                             = 364;
    public const T_ARRAY                                            = 365;
    public const T_CALLABLE                                         = 366;
    public const T_LINE                                             = 367;
    public const T_FILE                                             = 368;
    public const T_DIR                                              = 369;
    public const T_CLASS_C                                          = 370;
    public const T_TRAIT_C                                          = 371;
    public const T_METHOD_C                                         = 372;
    public const T_FUNC_C                                           = 373;
    public const T_COMMENT                                          = 374;
    public const T_DOC_COMMENT                                      = 375;
    public const T_OPEN_TAG                                         = 376;
    public const T_OPEN_TAG_WITH_ECHO                               = 377;
    public const T_CLOSE_TAG                                        = 378;
    public const T_WHITESPACE                                       = 379;
    public const T_START_HEREDOC                                    = 380;
    public const T_END_HEREDOC                                      = 381;
    public const T_DOLLAR_OPEN_CURLY_BRACES                         = 382;
    public const T_CURLY_OPEN                                       = 383;
    public const T_PAAMAYIM_NEKUDOTAYIM                             = 384;
    public const T_NAMESPACE                                        = 385;
    public const T_NS_C                                             = 386;
    public const T_NS_SEPARATOR                                     = 387;
    public const T_ELLIPSIS                                         = 388;
    public const T_DOUBLE_COLON                                     = 384;
    public const T_COALESCE_EQUAL                                   = 1000;
    public const T_FN                                               = 1000;
    public const T_NAME_FULLY_QUALIFIED                             = 1000;
    public const T_NAME_RELATIVE                                    = 1000;
    public const T_NAME_QUALIFIED                                   = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR                         = 1000;
    public const T_MATCH                                            = 1000;
    public const T_ATTRIBUTE                                        = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
    public const T_BAD_CHARACTER                                    = 1000;
    public const T_CHARACTER                                        = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Encoding extends Plugin {
    public function run(Atom $atom, array $extras): void {
        if (!function_exists('mb_detect_encoding')) {
            return;
        }

        switch ($atom->atom) {
            case 'Identifier' :
                $atom->encoding = mb_detect_encoding($atom->noDelimiter);
                if ($atom->encoding === 'UTF-8') {
                    $blocks = unicode_blocks($atom->noDelimiter);
                    $atom->block = array_keys($blocks)[0] ?? '';
                }
                break;

            case 'String' :
                // Case of a Quoted string " $a "
                if (!empty($extras)) {
                    $atom->encoding    = 'none';
                    $atom->block       = 'none';
                    break;
                }

                $atom->encoding = mb_detect_encoding($atom->noDelimiter);
                if ($atom->encoding === 'UTF-8') {
                    $blocks = unicode_blocks($atom->noDelimiter);
                    $atom->block = array_keys($blocks)[0] ?? '';
                }
                break;

        default :
            // Nothing, really
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php74 extends Php {

    // PHP tokens
    public const T_INCLUDE                       = 259;
    public const T_INCLUDE_ONCE                  = 260;
    public const T_REQUIRE                       = 261;
    public const T_REQUIRE_ONCE                  = 262;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_YIELD                         = 267;
    public const T_DOUBLE_ARROW                  = 268;
    public const T_YIELD_FROM                    = 269;
    public const T_PLUS_EQUAL                    = 270;
    public const T_MINUS_EQUAL                   = 271;
    public const T_MUL_EQUAL                     = 272;
    public const T_DIV_EQUAL                     = 273;
    public const T_CONCAT_EQUAL                  = 274;
    public const T_MOD_EQUAL                     = 275;
    public const T_AND_EQUAL                     = 276;
    public const T_OR_EQUAL                      = 277;
    public const T_XOR_EQUAL                     = 278;
    public const T_SL_EQUAL                      = 279;
    public const T_SR_EQUAL                      = 280;
    public const T_POW_EQUAL                     = 281;
    public const T_COALESCE_EQUAL                = 282;
    public const T_COALESCE                      = 283;
    public const T_BOOLEAN_OR                    = 284;
    public const T_BOOLEAN_AND                   = 285;
    public const T_IS_EQUAL                      = 286;
    public const T_IS_NOT_EQUAL                  = 287;
    public const T_IS_IDENTICAL                  = 288;
    public const T_IS_NOT_IDENTICAL              = 289;
    public const T_SPACESHIP                     = 290;
    public const T_IS_SMALLER_OR_EQUAL           = 291;
    public const T_IS_GREATER_OR_EQUAL           = 292;
    public const T_SL                            = 293;
    public const T_SR                            = 294;
    public const T_INSTANCEOF                    = 295;
    public const T_INT_CAST                      = 296;
    public const T_DOUBLE_CAST                   = 297;
    public const T_STRING_CAST                   = 298;
    public const T_ARRAY_CAST                    = 299;
    public const T_OBJECT_CAST                   = 300;
    public const T_BOOL_CAST                     = 301;
    public const T_UNSET_CAST                    = 302;
    public const T_POW                           = 303;
    public const T_NEW                           = 304;
    public const T_CLONE                         = 305;
    public const T_ELSEIF                        = 307;
    public const T_ELSE                          = 308;
    public const T_LNUMBER                       = 309;
    public const T_DNUMBER                       = 310;
    public const T_STRING                        = 311;
    public const T_VARIABLE                      = 312;
    public const T_INLINE_HTML                   = 313;
    public const T_ENCAPSED_AND_WHITESPACE       = 314;
    public const T_CONSTANT_ENCAPSED_STRING      = 315;
    public const T_STRING_VARNAME                = 316;
    public const T_NUM_STRING                    = 317;
    public const T_EVAL                          = 318;
    public const T_INC                           = 319;
    public const T_DEC                           = 320;
    public const T_EXIT                          = 321;
    public const T_IF                            = 322;
    public const T_ENDIF                         = 323;
    public const T_ECHO                          = 324;
    public const T_DO                            = 325;
    public const T_WHILE                         = 326;
    public const T_ENDWHILE                      = 327;
    public const T_FOR                           = 328;
    public const T_ENDFOR                        = 329;
    public const T_FOREACH                       = 330;
    public const T_ENDFOREACH                    = 331;
    public const T_DECLARE                       = 332;
    public const T_ENDDECLARE                    = 333;
    public const T_AS                            = 334;
    public const T_SWITCH                        = 335;
    public const T_ENDSWITCH                     = 336;
    public const T_CASE                          = 337;
    public const T_DEFAULT                       = 338;
    public const T_BREAK                         = 339;
    public const T_CONTINUE                      = 340;
    public const T_GOTO                          = 341;
    public const T_FUNCTION                      = 342;
    public const T_FN                            = 343;
    public const T_CONST                         = 344;
    public const T_RETURN                        = 345;
    public const T_TRY                           = 346;
    public const T_CATCH                         = 347;
    public const T_FINALLY                       = 348;
    public const T_THROW                         = 349;
    public const T_USE                           = 350;
    public const T_INSTEADOF                     = 351;
    public const T_GLOBAL                        = 352;
    public const T_STATIC                        = 353;
    public const T_ABSTRACT                      = 354;
    public const T_FINAL                         = 355;
    public const T_PRIVATE                       = 356;
    public const T_PROTECTED                     = 357;
    public const T_PUBLIC                        = 358;
    public const T_VAR                           = 359;
    public const T_UNSET                         = 360;
    public const T_ISSET                         = 361;
    public const T_EMPTY                         = 362;
    public const T_HALT_COMPILER                 = 363;
    public const T_CLASS                         = 364;
    public const T_TRAIT                         = 365;
    public const T_INTERFACE                     = 366;
    public const T_EXTENDS                       = 367;
    public const T_IMPLEMENTS                    = 368;
    public const T_OBJECT_OPERATOR               = 369;
    public const T_LIST                          = 370;
    public const T_ARRAY                         = 371;
    public const T_CALLABLE                      = 372;
    public const T_LINE                          = 373;
    public const T_FILE                          = 374;
    public const T_DIR                           = 375;
    public const T_CLASS_C                       = 376;
    public const T_TRAIT_C                       = 377;
    public const T_METHOD_C                      = 378;
    public const T_FUNC_C                        = 379;
    public const T_COMMENT                       = 380;
    public const T_DOC_COMMENT                   = 381;
    public const T_OPEN_TAG                      = 382;
    public const T_OPEN_TAG_WITH_ECHO            = 383;
    public const T_CLOSE_TAG                     = 384;
    public const T_WHITESPACE                    = 385;
    public const T_START_HEREDOC                 = 386;
    public const T_END_HEREDOC                   = 387;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 388;
    public const T_CURLY_OPEN                    = 389;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 390;
    public const T_NAMESPACE                     = 391;
    public const T_NS_C                          = 392;
    public const T_NS_SEPARATOR                  = 393;
    public const T_ELLIPSIS                      = 394;
    public const T_BAD_CHARACTER                 = 395;
    public const T_DOUBLE_COLON                  = 390;
    public const T_NAME_FULLY_QUALIFIED          = 1000;
    public const T_NAME_RELATIVE                 = 1000;
    public const T_NAME_QUALIFIED                = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR      = 1000;
    public const T_MATCH                         = 1000;
    public const T_ATTRIBUTE                     = 1000;
    public const T_CHARACTER                     = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class ContextVariables {
    private $list = array();

    public function set(string $name, AtomInterface $atom): void {
        // A variable may be redefined later (case of $a = 1; fn($a) => $a;)
        $this->list[$name] = $atom;
    }

    public function get(string $name): AtomInterface {
        assert(isset($this->list[$name]), "Get a undefined variable $name");
        return $this->list[$name];
    }

    public function exists(string $name): bool {
        return isset($this->list[$name]);
    }


}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Calls {
    public const A_CLASS        = 'class';
    public const FUNCTION       = 'function';
    public const CONST          = 'const';
    public const STATICCONSTANT = 'staticconstant';
    public const STATICMETHOD   = 'staticmethod';
    public const STATICPROPERTY = 'staticproperty';
    public const METHOD         = 'method';
    public const PROPERTY       = 'property';
    public const GOTO           = 'goto';

    public const ALL = array(self::A_CLASS,
                        self::FUNCTION,
                        self::CONST,
                        self::STATICCONSTANT,
                        self::STATICMETHOD,
                        self::STATICPROPERTY,
                        self::METHOD,
                        self::PROPERTY,
                        self::GOTO,
                       );

    private $callsSqlite   = null;

    private $definitions = array();
    private $calls       = array();

    public function __construct(\Sqlite3 $sqlite) {
        $this->callsSqlite = $sqlite;

        $calls = <<<'SQL'
CREATE TABLE IF NOT EXISTS calls (
    type STRING,
    fullnspath STRING,
    globalpath STRING,
    atom STRING,
    id INTEGER
)
SQL;
        $this->callsSqlite->query($calls);

        $definitions = <<<'SQL'
CREATE TABLE IF NOT EXISTS definitions (
    type STRING,
    fullnspath STRING,
    globalpath STRING,
    atom STRING,
    id INTEGER
)
SQL;
        $this->callsSqlite->query($definitions);
    }

    public function reset(): void {
        $this->calls       = array();
        $this->definitions = array();
        $this->globals     = array();
    }

    public function save(): void {
        if (!empty($this->calls)) {
            $chunks = array_chunk($this->calls, SQLITE_CHUNK_SIZE);
            foreach($chunks as $chunk) {
                $query = 'INSERT INTO calls VALUES ' . implode(', ', $chunk);
                $this->callsSqlite->query($query);
            }
            $this->calls = array();
        }

        if (!empty($this->definitions)) {
            $chunks = array_chunk($this->definitions, SQLITE_CHUNK_SIZE);
            foreach($chunks as $chunk) {
                $query = 'INSERT INTO definitions VALUES ' . implode(', ', $chunk);
                $this->callsSqlite->query($query);
            }
            $this->definitions = array();
        }

        if (!empty($this->globals)) {
            $chunks = array_chunk($this->globals, SQLITE_CHUNK_SIZE);
            foreach($chunks as $chunk) {
                $query = 'INSERT INTO globals VALUES ' . implode(', ', $chunk);
                $this->callsSqlite->query($query);
            }
            $this->globals = array();
        }
    }

    public function addCall(string $type, string $fullnspath, AtomInterface $call): void {
        assert(in_array($type, self::ALL), "Unknown call type : $type\n");

        if (empty($fullnspath)) {
            return;
        }

        // No need for This
        if (in_array($call->atom, array('Parent',
                                        'Isset',
                                        'List',
                                        'Empty',
                                        'Eval',
                                        'Exit',
                                        ))) {
            return;
        }

        if ($type === 'class') {
            $globalpath = $fullnspath;
        } elseif ($call->absolute === true) {
            $globalpath = $fullnspath;
        } else {
            $globalpath = $this->makeGlobalPath($fullnspath);
        }

        $this->calls[] = "('{$type}',
                           '{$this->callsSqlite->escapeString($fullnspath)}',
                           '{$this->callsSqlite->escapeString($globalpath)}',
                           '{$call->atom}',
                           '{$call->id}')";
    }

    public function addNoDelimiterCall(Atom $call): void {
        if (empty($call->noDelimiter)) {
            return; // Can't be a class anyway.
        }
        if ((int) $call->noDelimiter !== 0) {
            return; // Can't be a class anyway.
        }
        // single : is OK
        // \ is OK (for hardcoded path)
        if (preg_match_all('/[$ #?;%^\*\'\"\. <>~&,|\(\){}\[\]\/\s=\+!`@\-]/is', $call->noDelimiter, $r)) {
            return; // Can't be a class anyway.
        }

        if (strpos($call->noDelimiter, '::') === false) {
            $types = array('function', 'class');

            $fullnspath = mb_strtolower($call->noDelimiter);
            if (empty($fullnspath) || $fullnspath[0] !== '\\') {
                $fullnspath = '\\' . $fullnspath;
            }
            if (strpos($fullnspath, '\\\\') !== false) {
                $fullnspath = stripslashes($fullnspath);
            }
        } else {
            $fullnspath = mb_strtolower($call->noDelimiter);

            if (empty($fullnspath)) {
                return;
            } elseif ($fullnspath[0] === ':') {
                return;
            } elseif ($fullnspath[0] !== '\\') {
                $fullnspath = '\\' . $fullnspath;
            }

            $types = array('staticmethod', 'staticconstant');
        }

        $atom = 'String';

        foreach($types as $type) {
            $globalpath = $this->makeGlobalPath($fullnspath);

            $this->calls[] = "('$type',
                               '{$this->callsSqlite->escapeString($fullnspath)}',
                               '{$this->callsSqlite->escapeString($globalpath)}',
                               '{$atom}',
                               '{$call->id}')";
        }
    }

    public function addDefinition(string $type, string $fullnspath, AtomInterface $definition): void {
        assert(in_array($type, self::ALL), "Unknown definition type : $type\n");
        if (empty($fullnspath)) {
            return;
        }

        $globalpath = $this->makeGlobalPath($fullnspath);

        $this->definitions[] = "('{$type}',
                                 '{$this->callsSqlite->escapeString($fullnspath)}',
                                 '{$this->callsSqlite->escapeString($globalpath)}',
                                 '{$definition->atom}',
                                 '{$definition->id}')";
    }

    private function makeGlobalPath(string $fullnspath): string {
        if ($fullnspath === 'undefined') {
            $globalpath = '';
        } elseif (preg_match('/(\\\\[^\\\\]+)$/', $fullnspath, $r)) {
            $globalpath = $r[1];
        } else {
            $globalpath = substr($fullnspath, (int) strrpos($fullnspath, '\\'));
        }

        return $globalpath;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class ClassTraitContext {
    public const NO_CLASS_TRAIT_CONTEXT = null;

    private array          $contexts = array();
    private ?AtomInterface $last     = self::NO_CLASS_TRAIT_CONTEXT;

    public function __construct() {    }

    public function getCurrent(): ?AtomInterface {
        return $this->last;
    }

    public function pushContext(?AtomInterface $context): void {
        $this->contexts[] = $context;
        $this->last = $context;
    }

    public function popContext() {
        array_pop($this->contexts);
        $this->last = end($this->contexts) ?: self::NO_CLASS_TRAIT_CONTEXT;
    }

}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class IsPhp extends Plugin {
    public $name = 'isPhp';
    public $type = 'boolean';
    private array $phpFunctions        = array();
    private array $phpConstants        = array();
    private array $phpClasses          = array();
    private array $phpInterfaces       = array();
    private array $phpEnums            = array();
    private array $phpClassConstants   = array();
    private array $phpClassMethods     = array();
    private array $phpClassProperties  = array();
    private array $phpTraits           = array();

    public function __construct() {
        parent::__construct();

        $php = exakat('phpCore');
        $this->phpConstants = $php->getConstantList();
        $this->phpConstants = makeFullNsPath($this->phpConstants, \FNP_CONSTANT);

        $this->phpFunctions = $php->getFunctionList();
        $this->phpFunctions = makeFullNsPath($this->phpFunctions);

        $this->phpClasses = $php->getClassList();
        $this->phpClasses = makeFullNsPath($this->phpClasses);

        $this->phpInterfaces = $php->getInterfaceList();
        $this->phpInterfaces = makeFullNsPath($this->phpInterfaces);

        $this->phpTraits = $php->getTraitList();
        $this->phpTraits = makeFullNsPath($this->phpTraits);

        $this->phpEnums = $php->getEnumList();
        $this->phpEnums = makeFullNsPath($this->phpEnums);

        $this->phpClassConstants        = array_merge($php->getClassConstantList(),
                                                      $php->getEnumCasesList());
        $this->phpClassStaticProperties = $php->getClassStaticPropertyList();
        $this->phpClassMethods          = $php->getClassMethodList();
        $this->phpClassStaticMethods    = $php->getClassStaticMethodList();
    }

    public function run(Atom $atom, array $extras): void {
        $id = strrpos($atom->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
        $path = substr($atom->fullnspath ?? self::NOT_PROVIDED, $id);

        switch ($atom->atom) {
            case 'Methodcall' :
                $path = makeFullNsPath($extras['OBJECT']->fullnspath ?? self::NOT_PROVIDED);
                $method = mb_strtolower(substr($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, 0, strpos($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, '(') ?: 0));
                if (in_array($method, $this->phpClassMethods[$path] ?? array(), \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'Member' :
                $path = makeFullNsPath($extras['OBJECT']->fullnspath ?? self::NOT_PROVIDED);
                if (in_array($extras['MEMBER']->code ?? self::NOT_PROVIDED, $this->phpClassProperties[$path] ?? array(), \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'Staticmethodcall' :
                assert(!empty($extras), 'empty Extras in isPhp::StaticMethodcall' . print_r($atom, true));

                $path = makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED);
                $method = mb_strtolower(substr($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, 0, strpos($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, '(') ?: 0));

                if (in_array($path . '::' . $method, $this->phpClassStaticMethods, \STRICT_COMPARISON)) {
                    $extras['CLASS']->isPhp = true;
                    $atom->isPhp = true;
                }
                break;

            case 'Staticclass' :
                $path = makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED);
                if (in_array($path, array_merge($this->phpClasses,
                                                $this->phpTraits,
                                                $this->phpInterfaces,
                                                $this->phpEnums,
                                                ), \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                    $extras['CLASS']->isPhp = true;
                }
                break;

            case 'Staticproperty' :
                assert(!empty($extras), 'empty Extras in isPhp::Staticproperty');

                $path = makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED);
                if (in_array($extras['MEMBER']->code ?? self::NOT_PROVIDED, $this->phpClassProperties[$path] ?? array(), \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'Staticconstant' :
                if (in_array($atom->fullnspath ?? self::NOT_PROVIDED, $this->phpClassConstants, \STRICT_COMPARISON)) {
                    $extras['CLASS']->isPhp = true;
                    $atom->isPhp = true;
                }
                break;

            case 'Functioncall' :
                if (empty($path)) {
                    break;
                }
                if (in_array(makeFullNsPath($path), $this->phpFunctions, \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'String' :
                if (in_array(makeFullNsPath($atom->noDelimiter), $this->phpFunctions, \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'Ppp' :
            case 'Parameter' :
                $this->checkType($extras['TYPEHINT'] ?? array());
                break;

            case 'Usenamespace' :
                foreach($extras as $extra) {
                    $id   = strrpos($extra->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
                    $path = substr($extra->fullnspath ?? self::NOT_PROVIDED, $id);

                    switch($extra->use) {
                        case 'function':
                            if (in_array(makeFullNsPath($path), $this->phpFunctions, \STRICT_COMPARISON)) {
                                $extra->isPhp = true;
                            }
                        break;

                        case 'const':
                            if (in_array(makeFullNsPath($path, \FNP_CONSTANT), $this->phpConstants, \STRICT_COMPARISON)) {
                                $extra->isPhp = true;
                            }
                        break;

                    default: // case class
                            if (in_array(makeFullNsPath($path), array_merge($this->phpClasses,
                                                                            $this->phpTraits,
                                                                            $this->phpInterfaces,
                                                                            $this->phpEnums,
                                                                                ), \STRICT_COMPARISON)) {
                                $extra->isPhp = true;
                            }
                    }
                }
                break;

            case 'Class' :
                if (isset($extras['ATTRIBUTE'])) {
                    foreach($extras['ATTRIBUTE'] as $extra) {
                        if (in_array($extra->fullnspath ?? self::NOT_PROVIDED, $this->phpClasses, \STRICT_COMPARISON)) {
                            $extra->isPhp = true;
                        }
                    }
                }
                // Fallthrough is OK

            case 'Classanonymous' :
                if (in_array($extras['EXTENDS']->fullnspath ?? self::NOT_PROVIDED, $this->phpClasses, \STRICT_COMPARISON)) {
                    $extras['EXTENDS']->isPhp = true;
                }

                foreach($extras['IMPLEMENTS'] ?? array() as $implements) {
                    if (in_array($implements->fullnspath ?? self::NOT_PROVIDED, $this->phpInterfaces, \STRICT_COMPARISON)) {
                        $implements->isPhp = true;
                    }
                }
                break;

            case 'Interface' :
                foreach($extras['EXTENDS'] as $extra) {
                    if (in_array($extra->fullnspath ?? self::NOT_PROVIDED, $this->phpInterfaces, \STRICT_COMPARISON)) {
                        $extra->isPhp = true;
                    }
                }

                break;

            case 'Constant' :
                $atom->isPhp = false;
                $extras['NAME']->isPhp = false;
                break;

            case 'Function' :
            case 'Closure' :
            case 'Arrowfunction' :
            case 'Method' :
            case 'Magicmethod' :
                $this->checkType($extras['RETURNTYPE'] ?? array());
                break;

            case 'Method' :
            case 'Magicmethod' :
                foreach($extras['RETURNTYPE'] ?? array() as $extra) {
                    $id   = strrpos($extra->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
                    $path = substr($extra->fullnspath ?? self::NOT_PROVIDED, $id);

                    if (in_array(makeFullNsPath($path), array_merge($this->phpClasses,
                                                                    $this->phpInterfaces,
                                                                    $this->phpEnums,
                                                                    ), \STRICT_COMPARISON)) {
                        $extra->isPhp = true;
                    }
                }
                break;

            case 'Catch' :
                foreach($extras as $extra) {
                    $path = $extra->fullnspath ?? self::NOT_PROVIDED;

                    if (in_array(makeFullNsPath($path), array_merge($this->phpClasses,
                                                                    $this->phpInterfaces,
                                                                    ), \STRICT_COMPARISON)) {
                        $extra->isPhp = true;
                    }

                    if (in_array(makeFullNsPath($path), $this->phpInterfaces, \STRICT_COMPARISON)) {
                        $extra->isPhp = true;
                    }
                }
                break;

            case 'Instanceof' :
                // Warning : atom->fullnspath for classes (no fallback)
                if (in_array(makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED), array_merge($this->phpClasses,
                                                                    $this->phpInterfaces,
                                                                    $this->phpEnums,
                                                                    ), \STRICT_COMPARISON)) {
                    $extras['CLASS']->isPhp = true;
                }
                break;

            case 'Newcallname' :
                if (in_array($atom->fullnspath, $this->phpClasses, \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'Newcall' :
                if (empty($path)) {
                    break;
                }
                // Warning : atom->fullnspath for classes (no fallback)
                if (in_array(makeFullNsPath($atom->fullnspath), $this->phpClasses, \STRICT_COMPARISON)) {
                    $atom->isPhp = true;
                }
                break;

            case 'Identifier' :
            case 'Nsname' :
            case 'As' :
                if (empty($path)) {
                    return;
                }

                if ($atom->use === 'const') {
                    if (in_array($path, $this->phpConstants, \STRICT_COMPARISON) &&
                        strpos($atom->fullcode, '\\', 1) === false                ) {
                        $atom->isPhp = true;
                    }
                } elseif ($atom->use === 'function') {
                    if (in_array($path, $this->phpFunctions, \STRICT_COMPARISON) &&
                        strpos($atom->fullcode, '\\', 1) === false                ) {
                        $atom->isPhp = true;
                    }
                } elseif ($atom->use === 'class') {
                    $cit = array_merge($this->phpClasses,
                                       $this->phpInterfaces,
                                       $this->phpTraits,
                                       $this->phpEnums,
                                       );
                    if (in_array($path, $cit, \STRICT_COMPARISON) &&
                        strpos($atom->fullcode, '\\', 1) === false                ) {
                        $atom->isPhp = true;
                    }
                } else {
                    // This is the default behavior
                    if (in_array($path, $this->phpConstants, \STRICT_COMPARISON) &&
                        strpos($atom->fullcode, '\\', 1) === false) { // No extra \\, besides the first one
                        $atom->isPhp = true;
                    }
                }

                break;

            case 'Isset' :
            case 'Empty' :
            case 'Unset' :
            case 'Exit'  :
            case 'Echo'  :
            case 'Print' :
                $atom->isPhp = true;
                break;

            default :
                // Nothing
        }
    }

    private function checkType(array $extras): void {
        foreach($extras as $extra) {
            $id   = strrpos($extra->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
            $path = substr($extra->fullnspath ?? self::NOT_PROVIDED, $id);

            if (in_array(makeFullNsPath($path), $this->phpClasses, \STRICT_COMPARISON)) {
                $extra->isPhp = true;
            }

            if (in_array(makeFullNsPath($path), $this->phpInterfaces, \STRICT_COMPARISON)) {
                $extra->isPhp = true;
            }

            if (in_array($extra->fullnspath, $this->phpClasses, \STRICT_COMPARISON)) {
                $extra->isPhp = true;
            }

            if (in_array($extra->fullnspath, $this->phpInterfaces, \STRICT_COMPARISON)) {
                $extra->isPhp = true;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php53 extends Php {
    // PHP tokens
    public const T_REQUIRE_ONCE                  = 258;
    public const T_REQUIRE                       = 259;
    public const T_EVAL                          = 260;
    public const T_INCLUDE_ONCE                  = 261;
    public const T_INCLUDE                       = 262;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_SR_EQUAL                      = 267;
    public const T_SL_EQUAL                      = 268;
    public const T_XOR_EQUAL                     = 269;
    public const T_OR_EQUAL                      = 270;
    public const T_AND_EQUAL                     = 271;
    public const T_MOD_EQUAL                     = 272;
    public const T_CONCAT_EQUAL                  = 273;
    public const T_DIV_EQUAL                     = 274;
    public const T_MUL_EQUAL                     = 275;
    public const T_MINUS_EQUAL                   = 276;
    public const T_PLUS_EQUAL                    = 277;
    public const T_BOOLEAN_OR                    = 278;
    public const T_BOOLEAN_AND                   = 279;
    public const T_IS_NOT_IDENTICAL              = 280;
    public const T_IS_IDENTICAL                  = 281;
    public const T_IS_NOT_EQUAL                  = 282;
    public const T_IS_EQUAL                      = 283;
    public const T_IS_GREATER_OR_EQUAL           = 284;
    public const T_IS_SMALLER_OR_EQUAL           = 285;
    public const T_SR                            = 286;
    public const T_SL                            = 287;
    public const T_INSTANCEOF                    = 288;
    public const T_UNSET_CAST                    = 289;
    public const T_BOOL_CAST                     = 290;
    public const T_OBJECT_CAST                   = 291;
    public const T_ARRAY_CAST                    = 292;
    public const T_STRING_CAST                   = 293;
    public const T_DOUBLE_CAST                   = 294;
    public const T_INT_CAST                      = 295;
    public const T_DEC                           = 296;
    public const T_INC                           = 297;
    public const T_CLONE                         = 298;
    public const T_NEW                           = 299;
    public const T_EXIT                          = 300;
    public const T_IF                            = 301;
    public const T_ELSEIF                        = 302;
    public const T_ELSE                          = 303;
    public const T_ENDIF                         = 304;
    public const T_LNUMBER                       = 305;
    public const T_DNUMBER                       = 306;
    public const T_STRING                        = 307;
    public const T_STRING_VARNAME                = 308;
    public const T_VARIABLE                      = 309;
    public const T_NUM_STRING                    = 310;
    public const T_INLINE_HTML                   = 311;
    public const T_CHARACTER                     = 312;
    public const T_BAD_CHARACTER                 = 313;
    public const T_ENCAPSED_AND_WHITESPACE       = 314;
    public const T_CONSTANT_ENCAPSED_STRING      = 315;
    public const T_ECHO                          = 316;
    public const T_DO                            = 317;
    public const T_WHILE                         = 318;
    public const T_ENDWHILE                      = 319;
    public const T_FOR                           = 320;
    public const T_ENDFOR                        = 321;
    public const T_FOREACH                       = 322;
    public const T_ENDFOREACH                    = 323;
    public const T_DECLARE                       = 324;
    public const T_ENDDECLARE                    = 325;
    public const T_AS                            = 326;
    public const T_SWITCH                        = 327;
    public const T_ENDSWITCH                     = 328;
    public const T_CASE                          = 329;
    public const T_DEFAULT                       = 330;
    public const T_BREAK                         = 331;
    public const T_CONTINUE                      = 332;
    public const T_GOTO                          = 333;
    public const T_FUNCTION                      = 334;
    public const T_CONST                         = 335;
    public const T_RETURN                        = 336;
    public const T_TRY                           = 337;
    public const T_CATCH                         = 338;
    public const T_THROW                         = 339;
    public const T_USE                           = 340;
    public const T_GLOBAL                        = 341;
    public const T_PUBLIC                        = 342;
    public const T_PROTECTED                     = 343;
    public const T_PRIVATE                       = 344;
    public const T_FINAL                         = 345;
    public const T_ABSTRACT                      = 346;
    public const T_STATIC                        = 347;
    public const T_VAR                           = 348;
    public const T_UNSET                         = 349;
    public const T_ISSET                         = 350;
    public const T_EMPTY                         = 351;
    public const T_HALT_COMPILER                 = 352;
    public const T_CLASS                         = 353;
    public const T_INTERFACE                     = 354;
    public const T_EXTENDS                       = 355;
    public const T_IMPLEMENTS                    = 356;
    public const T_OBJECT_OPERATOR               = 357;
    public const T_DOUBLE_ARROW                  = 358;
    public const T_LIST                          = 359;
    public const T_ARRAY                         = 360;
    public const T_CLASS_C                       = 361;
    public const T_METHOD_C                      = 362;
    public const T_FUNC_C                        = 363;
    public const T_LINE                          = 364;
    public const T_FILE                          = 365;
    public const T_COMMENT                       = 366;
    public const T_DOC_COMMENT                   = 367;
    public const T_OPEN_TAG                      = 368;
    public const T_OPEN_TAG_WITH_ECHO            = 369;
    public const T_CLOSE_TAG                     = 370;
    public const T_WHITESPACE                    = 371;
    public const T_START_HEREDOC                 = 372;
    public const T_END_HEREDOC                   = 373;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 374;
    public const T_CURLY_OPEN                    = 375;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 376;
    public const T_NAMESPACE                     = 377;
    public const T_NS_C                          = 378;
    public const T_DIR                           = 379;
    public const T_NS_SEPARATOR                  = 380;
    public const T_DOUBLE_COLON                  = 376;

    public const T_SPACESHIP                     = 1000;
    public const T_YIELD_FROM                    = 1000;
    public const T_COALESCE                      = 1000;
    public const T_COALESCE_EQUAL                = 1000;
    public const T_FN                            = 1000;
    public const T_POW_EQUAL                     = 1000;
    public const T_POW                           = 1000;
    public const T_ELLIPSIS                      = 1000;
    public const T_NAME_FULLY_QUALIFIED          = 1000;
    public const T_NAME_RELATIVE                 = 1000;
    public const T_NAME_QUALIFIED                = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR      = 1000;
    public const T_MATCH                         = 1000;
    public const T_ATTRIBUTE                     = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare (strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

use Exakat\Exceptions\NoPrecedence;

class Precedence {
    public const WITH_SELF = true;
    public const WITHOUT_SELF = false;

    private $precedence  = array();
    private $definitions = array(
                        'T_OBJECT_OPERATOR'             => 0,
                        'T_DOUBLE_COLON'                => 0,
                        'T_DOLLAR'                      => 0,
                        'T_STATIC'                      => 0,
                        'T_EXIT'                        => 0,

                        'T_CLONE'                       => 1,
                        'T_NEW'                         => 1,

                        'T_OPEN_BRACKET'                => 2,

                        'T_POW'                         => 3,

                        'T_INC'                         => 4,
                        'T_DEC'                         => 4,

                        'T_SLASH'                       => 5,
                        'T_STAR'                        => 5,
                        'T_PERCENTAGE'                  => 5,

                        'T_ARRAY_CAST'                  => 6,
                        'T_BOOL_CAST'                   => 6,
                        'T_DOUBLE_CAST'                 => 6,
                        'T_INT_CAST'                    => 6,
                        'T_OBJECT_CAST'                 => 6,
                        'T_STRING_CAST'                 => 6,
                        'T_UNSET_CAST'                  => 6,
                        'T_AT'                          => 6,

                        'T_INSTANCEOF'                  => 7,

                        'T_TILDE'                       => 8,
                        'T_BANG'                        => 8,
                        'T_REFERENCE'                   => 8, // Special for reference's usage of &

                        'T_PLUS'                        => 18,
                        'T_MINUS'                       => 18,
                        'T_DOT'                         => 18, // Changed at constructor time for 21

                        'T_SR'                          => 20,
                        'T_SL'                          => 20,

                        'T_IS_SMALLER_OR_EQUAL'         => 30,
                        'T_IS_GREATER_OR_EQUAL'         => 30,
                        'T_GREATER'                     => 30,
                        'T_SMALLER'                     => 30,

                        'T_IS_EQUAL'                    => 37,
                        'T_IS_NOT_EQUAL'                => 37, // Double operator
                        'T_IS_IDENTICAL'                => 37,
                        'T_IS_NOT_IDENTICAL'            => 37,
                        'T_SPACESHIP'                   => 37,


                        'T_AND'                         => 42,    // &
                        'T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG' => 42,    // & in PHP 8.1
                        'T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG' => 42,    // & in PHP 8.1

                        'T_XOR'                         => 43,    // ^

                        'T_OR'                          => 54,     // |

                        'T_BOOLEAN_AND'                 => 65, // &&

                        'T_BOOLEAN_OR'                  => 76, // ||

                        'T_COALESCE'                    => 87,

                        'T_QUESTION'                    => 98,

                        'T_EQUAL'                       => 99,
                        'T_PLUS_EQUAL'                  => 99,
                        'T_AND_EQUAL'                   => 99,
                        'T_CONCAT_EQUAL'                => 99,
                        'T_DIV_EQUAL'                   => 99,
                        'T_MINUS_EQUAL'                 => 99,
                        'T_MOD_EQUAL'                   => 99,
                        'T_MUL_EQUAL'                   => 99,
                        'T_OR_EQUAL'                    => 99,
                        'T_POW_EQUAL'                   => 99,
                        'T_SL_EQUAL'                    => 99,
                        'T_SR_EQUAL'                    => 99,
                        'T_XOR_EQUAL'                   => 99,
                        'T_COALESCE_EQUAL'              => 99,

                        'T_LOGICAL_AND'                 => 100, // and

                        'T_LOGICAL_XOR'                 => 101, // xor

                        'T_LOGICAL_OR'                  => 102, // or

                        'T_YIELD'                       => 110,
                        'T_YIELD_FROM'                  => 110,

                        'T_ECHO'                        => 110,
                        'T_HALT_COMPILER'               => 110,
                        'T_PRINT'                       => 110,
                        'T_INCLUDE'                     => 110,
                        'T_INCLUDE_ONCE'                => 110,
                        'T_REQUIRE'                     => 110,
                        'T_REQUIRE_ONCE'                => 110,
                        'T_DOUBLE_ARROW'                => 110,

                        'T_RETURN'                      => 121,
                        'T_THROW'                       => 121,
                        'T_COLON'                       => 121,
                        'T_COMMA'                       => 121,
                        'T_CLOSE_TAG'                   => 121,
                        'T_CLOSE_PARENTHESIS'           => 121,
                        'T_CLOSE_BRACKET'               => 121,
                        'T_CLOSE_CURLY'                 => 121,
                        'T_AS'                          => 121,
                        'T_CONTINUE'                    => 121,
                        'T_BREAK'                       => 121,
                        'T_ELLIPSIS'                    => 121,
                        'T_GOTO'                        => 121,
                        'T_INSTEADOF'                   => 121,

                        'T_SEMICOLON'                   => 132,
    );

    private static $cache = array();

    public function __construct(string $phpVersion) {
        foreach($this->definitions as $name => $priority) {
            // Skip unknown tokens from other versions
            if (defined("$phpVersion::$name")) {
                $this->precedence[constant("$phpVersion::$name")] = $priority;
            }
        }

        if (substr($phpVersion, -2) === '80') {
            $this->precedence[constant("$phpVersion::T_DOT")] = 21;
        }

        foreach($this->precedence as $k1 => $p1) {
            self::$cache[$k1] = array();
            foreach($this->precedence as $k2 => $p2) {
                if ($p1 <= $p2 && $k1 !== $k2) {
                    self::$cache[$k1][] = $k2;
                }
            }
        }
    }

    public function get($token, bool $itself = self::WITHOUT_SELF): array {
        if (!isset(self::$cache[$token])) {
            throw new NoPrecedence($token);
        }

        if ($itself === self::WITH_SELF) {
            return array_merge(self::$cache[$token], array($token));
        } else {
            return self::$cache[$token];
        }
    }
}

?><?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Context {
    public const CONTEXT_CLASS        = 'Class';
    public const CONTEXT_INTERFACE    = 'Interface';
    public const CONTEXT_TRAIT        = 'Trait';
    public const CONTEXT_FUNCTION     = 'Function';
    public const CONTEXT_NEW          = 'New';
    public const CONTEXT_NOSEQUENCE   = 'NoSequence';
    public const CONTEXT_LIST         = 'List';
    private $contexts = array(self::CONTEXT_CLASS        => array(0),
                              self::CONTEXT_INTERFACE    => array(0),
                              self::CONTEXT_TRAIT        => array(0),
                              self::CONTEXT_FUNCTION     => array(0),
                              self::CONTEXT_NEW          => array(0),
                              self::CONTEXT_NOSEQUENCE   => array(0),
                              self::CONTEXT_LIST         => array(0),
                         );

    public function getCount(string $context = self::CONTEXT_NOSEQUENCE): bool {
        return $this->contexts[$context] !== array(0 => 0);
    }

    public function nestContext(string $context = self::CONTEXT_NOSEQUENCE): void {
        $this->contexts[$context][] = 0;
    }

    public function exitContext(string $context = self::CONTEXT_NOSEQUENCE): void {
        array_pop($this->contexts[$context]);
    }

    public function toggleContext(string $context): int {
        $toggled = 1 - $this->contexts[$context][count($this->contexts[$context]) - 1];
        $this->contexts[$context][count($this->contexts[$context]) - 1] = $toggled;
        return $toggled;
    }

    public function isContext(string $context): bool {
        return (bool) $this->contexts[$context][count($this->contexts[$context]) - 1];
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

use stdClass;

class Atom extends AtomInterface {
    public const STRING_MAX_SIZE = 500;

    public function __construct(int $id, string $atom, int $line, string $ws = '') {
        $this->id   = $id;
        $this->atom = $atom;
        $this->line = $line;
        $this->ws   = new Whitespace($ws);
    }

    public function __set($name, $value) {
        die("Fatal error : trying to set '$name' property on " . self::class);
    }

    public function __get($name) {
        die("Fatal error : trying to get '$name' property on " . self::class);
    }

    public function toArray(): array {
        if (strlen($this->code) > self::STRING_MAX_SIZE) {
            $this->code = substr($this->code, 0, self::STRING_MAX_SIZE) . '...[ total ' . strlen($this->code) . ' chars]';
        }
        if (strlen($this->lccode) > self::STRING_MAX_SIZE) {
            $this->lccode = substr($this->lccode, 0, self::STRING_MAX_SIZE) . '...[ total ' . strlen($this->lccode) . ' chars]';
        }
        if (strlen($this->fullcode) > self::STRING_MAX_SIZE) {
            $this->fullcode = substr($this->fullcode, 0, self::STRING_MAX_SIZE) . '...[ total ' . strlen($this->fullcode) . ' chars]';
        }

        $this->code          = $this->protectString($this->code       );
        $this->lccode        = $this->protectString($this->lccode     );
        $this->fullcode      = $this->protectString($this->fullcode   );
        $this->fullnspath    = $this->protectString($this->fullnspath );
        $this->strval        = $this->protectString($this->strval     );
        $this->noDelimiter   = $this->protectString($this->noDelimiter);
        $this->visibility    = $this->protectString($this->visibility );

        $this->alternative   = $this->alternative ? 1 : null;
        $this->reference     = $this->reference ? 1 : null;
        $this->heredoc       = $this->heredoc ? 1 : null;
        $this->variadic      = $this->variadic ? 1 : null;
        $this->final         = $this->final ? 1 : null;
        $this->abstract      = $this->abstract ? 1 : null;
        $this->readonly      = $this->readonly ? 1 : null;
        $this->static        = $this->static ? 1 : null;
        $this->absolute      = $this->absolute ? 1 : null;
        $this->constant      = $this->constant ? 1 : null;
        $this->boolean       = $this->boolean ? 1 : null;
        $this->enclosing     = $this->enclosing ? 1 : null;
        $this->bracket       = $this->bracket ? 1 : null;
        $this->flexible      = $this->flexible ? 1 : null;
        $this->close_tag     = $this->close_tag ? 1 : null;

        if ($this->intval > 2147483647) {
            $this->intval = 2147483647;
        }
        if ($this->intval < -2147483648) {
            $this->intval = -2147483648;
        }

        $this->globalvar     = !$this->globalvar ? null : $this->globalvar;

        return (array) $this;
    }

    public function toGraphsonLine(int &$id): stdClass {
        $integerValues = array('args_max',
                               'args_min',
                               'count',
                               'intval',
                               );

        $falseValues = array('globalvar',
                             );

        $properties = array();

        // The array list the properties that will be kept (except for default)
        $atomsValues = array('Sequence' => array('code'        => 0,
                                                 'line'        => 0,
                                                 'position'    => 0,
                                                 'count'       => 0,
                                                 'fullcode'    => 0,
                                                 'rank'        => 0,
                                                 'ws'          => 0,
                                                 ),

                             // This one is used to skip the values configure
                             'to_skip'  => array('id'          => 0,
                                                 'atom'        => 0,
                                                 'noscream'    => 0,
                                                 'reference'   => 0,
                                                 'variadic'    => 0,
                                                 'heredoc'     => 0,
                                                 'flexible'    => 0,
                                                 'constant'    => 0,
                                                 'enclosing'   => 0,
                                                 'final'       => 0,
                                                 'boolean'     => 0,
                                                 'bracket'     => 0,
                                                 'close_tag'   => 0,
                                                 'trailing'    => 0,
                                                 'alternative' => 0,
                                                 'absolute'    => 0,
                                                 'abstract'    => 0,
                                                 'readonly'    => 0,
                                                 'isRead'      => 0,
                                                 'isModified'  => 0,
                                                 'static'      => 0,
                                                 'isNull'      => 0,
                                                 'isPhp'       => 0,
                                                 'isExt'       => 0,
                                                 'isStub'      => 0,
                               )
                            );

        if (isset($atomsValues[$this->atom])) {
            $list = array_intersect_key((array) $this, $atomsValues[$this->atom]);
        } else {
            $list = array_diff_key((array) $this, $atomsValues['to_skip']);
        }

        foreach($list as $l => $value) {
            if ($value === null) { continue; }

            if (in_array($l, $falseValues) &&
                !$value) {
                continue;
            }

            if ($l === 'ws') {
                $value = $this->ws->toJson();
            }

            if ($l === 'lccode') {
                $this->lccode = mb_strtolower((string) $this->code);
                $value = $this->lccode;
            }

            if (!in_array($l, array('noDelimiter', 'lccode', 'code', 'fullcode', 'ws')) &&
                $value === '') {
                continue;
            }

            if ($value === false) {
                continue;
            }

            if (in_array($l, $integerValues)) {
                $value = (integer) $value;
            }

            $properties[$l] = array( new Property($id++, $value) );
        }

        $object = array('id'         => $this->id,
                        'label'      => $this->atom,
                        'inE'        => new \stdClass(),
                        'outE'       => new \stdClass(),
                        'properties' => $properties,
                        );

        return (object) $object;
    }

    public function boolProperties(): array {
        $return = array();
        foreach(array(
                 'noscream',
                 'reference',
                 'variadic',
                 'heredoc',
                 'flexible',
                 'constant',
                 'enclosing',
                 'final',
                 'boolean',
                 'bracket',
                 'close_tag',
                 'trailing',
                 'alternative',
                 'absolute',
                 'abstract',
                 'readonly',
                 'isRead',
                 'isModified',
                 'static',
                 'isNull',
                 'isPhp',
                 'isExt',
                 'isStub',
                               ) as $property) {
            if ($this->$property == true) {
                $return[] = $property;
            }
        }

        return $return;
    }

    private function protectString(string $code): string {
        return addcslashes($code , '\\"');
    }

    public function isA(array $atoms): bool {
        return in_array($this->atom, $atoms, \STRICT_COMPARISON);
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class IsStub extends Plugin {
    public $name = 'isStub';
    public $type = 'boolean';
    private array $stubConstants        = array();
    private array $stubFunctions        = array();
    private array $stubClasses          = array();
    private array $stubInterfaces       = array();
    private array $stubTraits           = array();
    private array $stubEnums            = array();

    private array $stubClassConstants   = array();
    private array $stubClassMethods     = array();
    private array $stubClassProperties  = array();

    private array $stubMethods          = array();
    private array $stubProperties       = array();

    public function __construct() {
        parent::__construct();

        $stubs  = exakat('stubs');

        $this->stubConstants = $stubs->getConstantList();
        $this->stubConstants = makeFullNsPath($this->stubConstants, \FNP_CONSTANT);

        $this->stubFunctions = $stubs->getFunctionList();
        $this->stubFunctions = makeFullNsPath($this->stubFunctions);

        $this->stubClasses = $stubs->getClassList();
        $this->stubClasses = makeFullNsPath($this->stubClasses);

        $this->stubInterfaces = $stubs->getInterfaceList();
        $this->stubInterfaces = makeFullNsPath($this->stubInterfaces);

        $this->stubTraits = $stubs->getTraitList();
        $this->stubTraits = makeFullNsPath($this->stubTraits);

        // cases and constants are mixed! NO way to disambiguate them later.
        $this->stubClassConstants   = array_merge($stubs->getClassConstantList(),
                                                  $stubs->getEnumCasesList());
        $this->stubProperties         = $stubs->getClassPropertyList();
        $this->stubStaticProperties   = $stubs->getClassStaticPropertyList();
        $this->stubMethods            = $stubs->getClassMethodList();
        $this->stubClassStaticMethods = $stubs->getClassStaticMethodList();
    }

    public function run(Atom $atom, array $extras): void {
        $id   = strrpos($atom->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
        $path = substr($atom->fullnspath  ?? self::NOT_PROVIDED, $id);

        switch ($atom->atom) {
            case 'Methodcall' :
                $path = makeFullNsPath($extras['OBJECT']->fullnspath ?? self::NOT_PROVIDED);
                $method = mb_strtolower(substr($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, 0, strpos($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, '(') ?: 0));
                if (in_array($method, $this->stubMethods[$path] ?? array(), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Member' :
                $path = makeFullNsPath($extras['OBJECT']->fullnspath ?? self::NOT_PROVIDED);
                if (in_array($extras['MEMBER']->code ?? self::NOT_PROVIDED, $this->stubProperties[$path] ?? array(), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Staticclass' :
                $path = makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED);
                if (in_array($path, array_merge($this->stubClasses,
                                                $this->stubTraits,
                                                $this->stubEnums,
                                                $this->stubInterfaces,
                                                ), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                    $extras['CLASS']->isStub = true;
                }
                break;

            case 'Staticmethodcall' :
                assert(!empty($extras), 'empty Extras in isStub::StaticMethodcall' . print_r($atom, true));
                $path = makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED);
                $method = mb_strtolower(substr($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, 0, strpos($extras['METHOD']->fullcode ?? self::NOT_PROVIDED, '(') ?: 0));
                if (in_array($path . '::' . $method, $this->stubClassStaticMethods, \STRICT_COMPARISON)) {
                    $extras['CLASS']->isPhp = true;
                    $atom->isPhp = true;
                }
                break;

            case 'Staticproperty' :
                $path = makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED);
                // @todo : this won't work, due tu [$path]
                if (in_array($extras['MEMBER']->code ?? self::NOT_PROVIDED, $this->stubClassProperties[$path] ?? array(), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }

                if (in_array($atom->fullnspath ?? self::NOT_PROVIDED, $this->stubStaticProperties ?? array(), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Staticconstant' :
                if (in_array($atom->fullnspath ?? self::NOT_PROVIDED, $this->stubClassConstants, \STRICT_COMPARISON)) {
                    $extras['CLASS']->isStub = true;
                    $atom->isStub = true;
                }
                break;

            case 'Functioncall' :
                if (empty($path)) {
                    break;
                }

                if (in_array(makeFullNsPath($path), $this->stubFunctions, \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }

                if (in_array($atom->fullnspath, $this->stubFunctions, \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'String' :
                if (in_array(makeFullNsPath($atom->noDelimiter), $this->stubFunctions, \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Ppp' :
            case 'Parameter' :
                $this->checkTypes($extras['TYPEHINT'] ?? array());
                break;

            case 'Usenamespace' :
                foreach($extras as $extra) {
                    $id   = strrpos($extra->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
                    $path = substr($extra->fullnspath ?? self::NOT_PROVIDED, $id);

                    switch($extra->use) {
                        case 'function':
                            if (in_array(makeFullNsPath($path), $this->stubFunctions, \STRICT_COMPARISON)) {
                                $extra->isStub = true;
                            }
                        break;

                        case 'const':
                            if (in_array(makeFullNsPath($path, \FNP_CONSTANT), $this->stubConstants, \STRICT_COMPARISON)) {
                                $extra->isStub = true;
                            }
                        break;

                    default: // case class
                            if (in_array(makeFullNsPath($path), array_merge($this->stubClasses,
                                                                            $this->stubTraits,
                                                                            $this->stubEnums,
                                                                            $this->stubInterfaces,
                                                                            ), \STRICT_COMPARISON)) {
                                $extra->isStub = true;
                            }
                    }
                }
                break;

            case 'Usetrait' :
                // for specific namespaces
                foreach($extras as $extra) {
                    if (in_array($extra->fullnspath, $this->stubTraits, \STRICT_COMPARISON)) {
                        $extra->isStub = true;
                    }
                }
                break;

            case 'Class' :
                if (isset($extras['ATTRIBUTE'])) {
                    foreach($extras['ATTRIBUTE'] as $extra) {
                        if (in_array($extra->fullnspath ?? self::NOT_PROVIDED, array_merge($this->stubClasses,
                                                                                           $this->stubTraits,
                                                                                           $this->stubEnums,
                                                                                           $this->stubInterfaces,
                                                                                           ), \STRICT_COMPARISON)) {
                            $extra->isStub = true;
                        }
                    }
                }
                // Fallthrough is OK

            //case 'Class' :
            case 'Classanonymous' :
                if (isset($extras['EXTENDS']) &&
                    in_array($extras['EXTENDS']->fullnspath ?? self::NOT_PROVIDED, array_merge($this->stubClasses,
                                                                                               $this->stubTraits,
                                                                                               $this->stubEnums,
                                                                                               $this->stubInterfaces,
                                                                                               ), \STRICT_COMPARISON)) {
                    $extras['EXTENDS']->isStub = true;
                }

                foreach($extras['IMPLEMENTS'] ?? array() as $implements) {
                    if (in_array($implements->fullnspath ?? self::NOT_PROVIDED, $this->stubInterfaces, \STRICT_COMPARISON)) {
                        $implements->isStub = true;
                    }
                }
                break;

            case 'Interface' :
                foreach($extras['EXTENDS'] as $extra) {
                    if (in_array($extra->fullnspath ?? self::NOT_PROVIDED, $this->stubInterfaces, \STRICT_COMPARISON)) {
                        $extra->isStub = true;
                    }
                }
                break;

            case 'Constant' :
                $atom->isStub = false;
                $extras['NAME']->isStub = false;
                break;

            case 'Instanceof' :
                // Warning : atom->fullnspath for classes (no fallback)
                if (in_array(makeFullNsPath($extras['CLASS']->fullnspath ?? self::NOT_PROVIDED), array_merge($this->stubClasses,
                                                                                                             $this->stubTraits,
                                                                                                             $this->stubEnums,
                                                                                                             $this->stubInterfaces,
                                                                                                             ), \STRICT_COMPARISON)) {
                    $extras['CLASS']->isStub = true;
                }
                break;

            case 'Parameter' :
                foreach($extras as $extra) {
                    if (in_array(makeFullNsPath($extra->fullnspath ?? self::NOT_PROVIDED), array_merge($this->stubClasses,
                                                                                                       $this->stubTraits,
                                                                                                       $this->stubEnums,
                                                                                                       $this->stubInterfaces,
                                                                                                       ), \STRICT_COMPARISON)) {
                        $extra->isStub = true;
                        $atom->isStub = true;
                    }
                }
                break;

            case 'Function' :
            case 'Closure' :
            case 'Arrowfunction' :
            case 'Method' :
            case 'Magicmethod' :
                $this->checkTypes($extras['RETURNTYPE'] ?? array());
                break;

            case 'Newcall' :
                if (empty($path)) {
                    break;
                }
                // Warning : atom->fullnspath for classes (no fallback)
                if (in_array(makeFullNsPath($atom->fullnspath), array_merge($this->stubClasses,
                                                                            $this->stubTraits,
                                                                            $this->stubEnums,
                                                                            $this->stubInterfaces,
                                                                            ), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Newcallname' :
                if (in_array($atom->fullnspath, array_merge($this->stubClasses,
                                                            $this->stubTraits,
                                                            $this->stubEnums,
                                                            $this->stubInterfaces,
                                                            ), \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Identifier' :
            case 'Nsname' :
                if (empty($path)) {
                    break;
                }

                // for fallback to main namespace
                if (in_array($path, $this->stubConstants, \STRICT_COMPARISON) &&
                    strpos($atom->fullcode, '\\', 1) === false) {
                    $atom->isStub = true;
                }

                // for specific namespaces
                if (in_array($atom->fullnspath, $this->stubConstants, \STRICT_COMPARISON)) {
                    $atom->isStub = true;
                }
                break;

            case 'Catch' :
                foreach($extras as $extra) {
                    $path = $extra->fullnspath ?? self::NOT_PROVIDED;

                    if (in_array(makeFullNsPath($path), array_merge($this->stubClasses,
                                                                    $this->stubTraits,
                                                                    $this->stubEnums,
                                                                    $this->stubInterfaces,
                                                                    ), \STRICT_COMPARISON)) {
                        $extra->isStub = true;
                    }

                    if (in_array(makeFullNsPath($path), $this->stubInterfaces, \STRICT_COMPARISON)) {
                        $extra->isStub = true;
                    }
                }
                break;

            case 'Isset' :
            case 'Unset' :
            case 'Exit'  :
            case 'Empty' :
            case 'Echo'  :
            case 'Print' :
                $atom->isStub = false;
                break;

            default :
                // Nothing
        }
    }

    private function checkTypes(array $extras): void {
        foreach($extras as $extra) {
            $id   = strrpos($extra->fullnspath ?? self::NOT_PROVIDED, '\\') ?: 0;
            $path = substr($extra->fullnspath ?? self::NOT_PROVIDED, $id);

            if (in_array(makeFullNsPath($path), array_merge($this->stubClasses,
                                                            $this->stubTraits,
                                                            $this->stubEnums,
                                                            $this->stubInterfaces,
                                                            ), \STRICT_COMPARISON)) {
                $extra->isStub = true;
            }

            if (in_array($extra->fullnspath, array_merge($this->stubClasses,
                                                         $this->stubTraits,
                                                         $this->stubEnums,
                                                         $this->stubInterfaces,
                                                         ), \STRICT_COMPARISON)) {
                $extra->isStub = true;
            }
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Property {
    public $id    = 0;
    public $value = 'No value set';

    public function __construct(int $id, $value) {
        $this->id    = $id;
        // $value might be an integer or a string
        $this->value = $value;
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class Php82 extends Php {

    // PHP tokens
    public const T_LNUMBER                                          = 260;
    public const T_DNUMBER                                          = 261;
    public const T_STRING                                           = 262;
    public const T_NAME_FULLY_QUALIFIED                             = 263;
    public const T_NAME_RELATIVE                                    = 264;
    public const T_NAME_QUALIFIED                                   = 265;
    public const T_VARIABLE                                         = 266;
    public const T_INLINE_HTML                                      = 267;
    public const T_ENCAPSED_AND_WHITESPACE                          = 268;
    public const T_CONSTANT_ENCAPSED_STRING                         = 269;
    public const T_STRING_VARNAME                                   = 270;
    public const T_NUM_STRING                                       = 271;
    public const T_INCLUDE                                          = 272;
    public const T_INCLUDE_ONCE                                     = 273;
    public const T_EVAL                                             = 274;
    public const T_REQUIRE                                          = 275;
    public const T_REQUIRE_ONCE                                     = 276;
    public const T_LOGICAL_OR                                       = 277;
    public const T_LOGICAL_XOR                                      = 278;
    public const T_LOGICAL_AND                                      = 279;
    public const T_PRINT                                            = 280;
    public const T_YIELD                                            = 281;
    public const T_YIELD_FROM                                       = 282;
    public const T_INSTANCEOF                                       = 283;
    public const T_NEW                                              = 284;
    public const T_CLONE                                            = 285;
    public const T_EXIT                                             = 286;
    public const T_IF                                               = 287;
    public const T_ELSEIF                                           = 288;
    public const T_ELSE                                             = 289;
    public const T_ENDIF                                            = 290;
    public const T_ECHO                                             = 291;
    public const T_DO                                               = 292;
    public const T_WHILE                                            = 293;
    public const T_ENDWHILE                                         = 294;
    public const T_FOR                                              = 295;
    public const T_ENDFOR                                           = 296;
    public const T_FOREACH                                          = 297;
    public const T_ENDFOREACH                                       = 298;
    public const T_DECLARE                                          = 299;
    public const T_ENDDECLARE                                       = 300;
    public const T_AS                                               = 301;
    public const T_SWITCH                                           = 302;
    public const T_ENDSWITCH                                        = 303;
    public const T_CASE                                             = 304;
    public const T_DEFAULT                                          = 305;
    public const T_MATCH                                            = 306;
    public const T_BREAK                                            = 307;
    public const T_CONTINUE                                         = 308;
    public const T_GOTO                                             = 309;
    public const T_FUNCTION                                         = 310;
    public const T_FN                                               = 311;
    public const T_CONST                                            = 312;
    public const T_RETURN                                           = 313;
    public const T_TRY                                              = 314;
    public const T_CATCH                                            = 315;
    public const T_FINALLY                                          = 316;
    public const T_THROW                                            = 317;
    public const T_USE                                              = 318;
    public const T_INSTEADOF                                        = 319;
    public const T_GLOBAL                                           = 320;
    public const T_STATIC                                           = 321;
    public const T_ABSTRACT                                         = 322;
    public const T_FINAL                                            = 323;
    public const T_PRIVATE                                          = 324;
    public const T_PROTECTED                                        = 325;
    public const T_PUBLIC                                           = 326;
    public const T_READONLY                                         = 327;
    public const T_VAR                                              = 328;
    public const T_UNSET                                            = 329;
    public const T_ISSET                                            = 330;
    public const T_EMPTY                                            = 331;
    public const T_HALT_COMPILER                                    = 332;
    public const T_CLASS                                            = 333;
    public const T_TRAIT                                            = 334;
    public const T_INTERFACE                                        = 335;
    public const T_ENUM                                             = 336;
    public const T_EXTENDS                                          = 337;
    public const T_IMPLEMENTS                                       = 338;
    public const T_NAMESPACE                                        = 339;
    public const T_LIST                                             = 340;
    public const T_ARRAY                                            = 341;
    public const T_CALLABLE                                         = 342;
    public const T_LINE                                             = 343;
    public const T_FILE                                             = 344;
    public const T_DIR                                              = 345;
    public const T_CLASS_C                                          = 346;
    public const T_TRAIT_C                                          = 347;
    public const T_METHOD_C                                         = 348;
    public const T_FUNC_C                                           = 349;
    public const T_NS_C                                             = 350;
    public const T_ATTRIBUTE                                        = 351;
    public const T_PLUS_EQUAL                                       = 352;
    public const T_MINUS_EQUAL                                      = 353;
    public const T_MUL_EQUAL                                        = 354;
    public const T_DIV_EQUAL                                        = 355;
    public const T_CONCAT_EQUAL                                     = 356;
    public const T_MOD_EQUAL                                        = 357;
    public const T_AND_EQUAL                                        = 358;
    public const T_OR_EQUAL                                         = 359;
    public const T_XOR_EQUAL                                        = 360;
    public const T_SL_EQUAL                                         = 361;
    public const T_SR_EQUAL                                         = 362;
    public const T_COALESCE_EQUAL                                   = 363;
    public const T_BOOLEAN_OR                                       = 364;
    public const T_BOOLEAN_AND                                      = 365;
    public const T_IS_EQUAL                                         = 366;
    public const T_IS_NOT_EQUAL                                     = 367;
    public const T_IS_IDENTICAL                                     = 368;
    public const T_IS_NOT_IDENTICAL                                 = 369;
    public const T_IS_SMALLER_OR_EQUAL                              = 370;
    public const T_IS_GREATER_OR_EQUAL                              = 371;
    public const T_SPACESHIP                                        = 372;
    public const T_SL                                               = 373;
    public const T_SR                                               = 374;
    public const T_INC                                              = 375;
    public const T_DEC                                              = 376;
    public const T_INT_CAST                                         = 377;
    public const T_DOUBLE_CAST                                      = 378;
    public const T_STRING_CAST                                      = 379;
    public const T_ARRAY_CAST                                       = 380;
    public const T_OBJECT_CAST                                      = 381;
    public const T_BOOL_CAST                                        = 382;
    public const T_UNSET_CAST                                       = 383;
    public const T_OBJECT_OPERATOR                                  = 384;
    public const T_NULLSAFE_OBJECT_OPERATOR                         = 385;
    public const T_DOUBLE_ARROW                                     = 386;
    public const T_COMMENT                                          = 387;
    public const T_DOC_COMMENT                                      = 388;
    public const T_OPEN_TAG                                         = 389;
    public const T_OPEN_TAG_WITH_ECHO                               = 390;
    public const T_CLOSE_TAG                                        = 391;
    public const T_WHITESPACE                                       = 392;
    public const T_START_HEREDOC                                    = 393;
    public const T_END_HEREDOC                                      = 394;
    public const T_DOLLAR_OPEN_CURLY_BRACES                         = 395;
    public const T_CURLY_OPEN                                       = 396;
    public const T_PAAMAYIM_NEKUDOTAYIM                             = 397;
    public const T_NS_SEPARATOR                                     = 398;
    public const T_ELLIPSIS                                         = 399;
    public const T_COALESCE                                         = 400;
    public const T_POW                                              = 401;
    public const T_POW_EQUAL                                        = 402;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 403;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 404;
    public const T_BAD_CHARACTER                                    = 405;
    public const T_DOUBLE_COLON                                     = 397;
    public const T_CHARACTER                                        = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php55 extends Php {

    // PHP tokens
    public const T_REQUIRE_ONCE                  = 258;
    public const T_REQUIRE                       = 259;
    public const T_EVAL                          = 260;
    public const T_INCLUDE_ONCE                  = 261;
    public const T_INCLUDE                       = 262;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_SR_EQUAL                      = 268;
    public const T_SL_EQUAL                      = 269;
    public const T_XOR_EQUAL                     = 270;
    public const T_OR_EQUAL                      = 271;
    public const T_AND_EQUAL                     = 272;
    public const T_MOD_EQUAL                     = 273;
    public const T_CONCAT_EQUAL                  = 274;
    public const T_DIV_EQUAL                     = 275;
    public const T_MUL_EQUAL                     = 276;
    public const T_MINUS_EQUAL                   = 277;
    public const T_PLUS_EQUAL                    = 278;
    public const T_BOOLEAN_OR                    = 279;
    public const T_BOOLEAN_AND                   = 280;
    public const T_IS_NOT_IDENTICAL              = 281;
    public const T_IS_IDENTICAL                  = 282;
    public const T_IS_NOT_EQUAL                  = 283;
    public const T_IS_EQUAL                      = 284;
    public const T_IS_GREATER_OR_EQUAL           = 285;
    public const T_IS_SMALLER_OR_EQUAL           = 286;
    public const T_SR                            = 287;
    public const T_SL                            = 288;
    public const T_INSTANCEOF                    = 289;
    public const T_UNSET_CAST                    = 290;
    public const T_BOOL_CAST                     = 291;
    public const T_OBJECT_CAST                   = 292;
    public const T_ARRAY_CAST                    = 293;
    public const T_STRING_CAST                   = 294;
    public const T_DOUBLE_CAST                   = 295;
    public const T_INT_CAST                      = 296;
    public const T_DEC                           = 297;
    public const T_INC                           = 298;
    public const T_CLONE                         = 299;
    public const T_NEW                           = 300;
    public const T_EXIT                          = 301;
    public const T_IF                            = 302;
    public const T_ELSEIF                        = 303;
    public const T_ELSE                          = 304;
    public const T_ENDIF                         = 305;
    public const T_LNUMBER                       = 306;
    public const T_DNUMBER                       = 307;
    public const T_STRING                        = 308;
    public const T_STRING_VARNAME                = 309;
    public const T_VARIABLE                      = 310;
    public const T_NUM_STRING                    = 311;
    public const T_INLINE_HTML                   = 312;
    public const T_CHARACTER                     = 313;
    public const T_BAD_CHARACTER                 = 314;
    public const T_ENCAPSED_AND_WHITESPACE       = 315;
    public const T_CONSTANT_ENCAPSED_STRING      = 316;
    public const T_ECHO                          = 317;
    public const T_DO                            = 318;
    public const T_WHILE                         = 319;
    public const T_ENDWHILE                      = 320;
    public const T_FOR                           = 321;
    public const T_ENDFOR                        = 322;
    public const T_FOREACH                       = 323;
    public const T_ENDFOREACH                    = 324;
    public const T_DECLARE                       = 325;
    public const T_ENDDECLARE                    = 326;
    public const T_AS                            = 327;
    public const T_SWITCH                        = 328;
    public const T_ENDSWITCH                     = 329;
    public const T_CASE                          = 330;
    public const T_DEFAULT                       = 331;
    public const T_BREAK                         = 332;
    public const T_CONTINUE                      = 333;
    public const T_GOTO                          = 334;
    public const T_FUNCTION                      = 335;
    public const T_CONST                         = 336;
    public const T_RETURN                        = 337;
    public const T_YIELD                         = 267;
    public const T_TRY                           = 338;
    public const T_CATCH                         = 339;
    public const T_FINALLY                       = 340;
    public const T_THROW                         = 341;
    public const T_USE                           = 342;
    public const T_INSTEADOF                     = 343;
    public const T_GLOBAL                        = 344;
    public const T_PUBLIC                        = 345;
    public const T_PROTECTED                     = 346;
    public const T_PRIVATE                       = 347;
    public const T_FINAL                         = 348;
    public const T_ABSTRACT                      = 349;
    public const T_STATIC                        = 350;
    public const T_VAR                           = 351;
    public const T_UNSET                         = 352;
    public const T_ISSET                         = 353;
    public const T_EMPTY                         = 354;
    public const T_HALT_COMPILER                 = 355;
    public const T_CLASS                         = 356;
    public const T_TRAIT                         = 357;
    public const T_INTERFACE                     = 358;
    public const T_EXTENDS                       = 359;
    public const T_IMPLEMENTS                    = 360;
    public const T_OBJECT_OPERATOR               = 361;
    public const T_DOUBLE_ARROW                  = 362;
    public const T_LIST                          = 363;
    public const T_ARRAY                         = 364;
    public const T_CALLABLE                      = 365;
    public const T_CLASS_C                       = 366;
    public const T_TRAIT_C                       = 367;
    public const T_METHOD_C                      = 368;
    public const T_FUNC_C                        = 369;
    public const T_LINE                          = 370;
    public const T_FILE                          = 371;
    public const T_COMMENT                       = 372;
    public const T_DOC_COMMENT                   = 373;
    public const T_OPEN_TAG                      = 374;
    public const T_OPEN_TAG_WITH_ECHO            = 375;
    public const T_CLOSE_TAG                     = 376;
    public const T_WHITESPACE                    = 377;
    public const T_START_HEREDOC                 = 378;
    public const T_END_HEREDOC                   = 379;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 380;
    public const T_CURLY_OPEN                    = 381;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 382;
    public const T_NAMESPACE                     = 383;
    public const T_NS_C                          = 384;
    public const T_DIR                           = 385;
    public const T_NS_SEPARATOR                  = 386;
    public const T_DOUBLE_COLON                  = 382;
    public const T_SPACESHIP                                        = 1000;
    public const T_YIELD_FROM                                       = 1000;
    public const T_COALESCE                                         = 1000;
    public const T_COALESCE_EQUAL                                   = 1000;
    public const T_FN                                               = 1000;
    public const T_POW_EQUAL                                        = 1000;
    public const T_POW                                              = 1000;
    public const T_ELLIPSIS                                         = 1000;
    public const T_NAME_FULLY_QUALIFIED                             = 1000;
    public const T_NAME_RELATIVE                                    = 1000;
    public const T_NAME_QUALIFIED                                   = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR                         = 1000;
    public const T_MATCH                                            = 1000;
    public const T_ATTRIBUTE                                        = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Php54 extends Php {
    // PHP tokens
    public const T_REQUIRE_ONCE                  = 258;
    public const T_REQUIRE                       = 259;
    public const T_EVAL                          = 260;
    public const T_INCLUDE_ONCE                  = 261;
    public const T_INCLUDE                       = 262;
    public const T_LOGICAL_OR                    = 263;
    public const T_LOGICAL_XOR                   = 264;
    public const T_LOGICAL_AND                   = 265;
    public const T_PRINT                         = 266;
    public const T_SR_EQUAL                      = 267;
    public const T_SL_EQUAL                      = 268;
    public const T_XOR_EQUAL                     = 269;
    public const T_OR_EQUAL                      = 270;
    public const T_AND_EQUAL                     = 271;
    public const T_MOD_EQUAL                     = 272;
    public const T_CONCAT_EQUAL                  = 273;
    public const T_DIV_EQUAL                     = 274;
    public const T_MUL_EQUAL                     = 275;
    public const T_MINUS_EQUAL                   = 276;
    public const T_PLUS_EQUAL                    = 277;
    public const T_BOOLEAN_OR                    = 278;
    public const T_BOOLEAN_AND                   = 279;
    public const T_IS_NOT_IDENTICAL              = 280;
    public const T_IS_IDENTICAL                  = 281;
    public const T_IS_NOT_EQUAL                  = 282;
    public const T_IS_EQUAL                      = 283;
    public const T_IS_GREATER_OR_EQUAL           = 284;
    public const T_IS_SMALLER_OR_EQUAL           = 285;
    public const T_SR                            = 286;
    public const T_SL                            = 287;
    public const T_INSTANCEOF                    = 288;
    public const T_UNSET_CAST                    = 289;
    public const T_BOOL_CAST                     = 290;
    public const T_OBJECT_CAST                   = 291;
    public const T_ARRAY_CAST                    = 292;
    public const T_STRING_CAST                   = 293;
    public const T_DOUBLE_CAST                   = 294;
    public const T_INT_CAST                      = 295;
    public const T_DEC                           = 296;
    public const T_INC                           = 297;
    public const T_CLONE                         = 298;
    public const T_NEW                           = 299;
    public const T_EXIT                          = 300;
    public const T_IF                            = 301;
    public const T_ELSEIF                        = 302;
    public const T_ELSE                          = 303;
    public const T_ENDIF                         = 304;
    public const T_LNUMBER                       = 305;
    public const T_DNUMBER                       = 306;
    public const T_STRING                        = 307;
    public const T_STRING_VARNAME                = 308;
    public const T_VARIABLE                      = 309;
    public const T_NUM_STRING                    = 310;
    public const T_INLINE_HTML                   = 311;
    public const T_CHARACTER                     = 312;
    public const T_BAD_CHARACTER                 = 313;
    public const T_ENCAPSED_AND_WHITESPACE       = 314;
    public const T_CONSTANT_ENCAPSED_STRING      = 315;
    public const T_ECHO                          = 316;
    public const T_DO                            = 317;
    public const T_WHILE                         = 318;
    public const T_ENDWHILE                      = 319;
    public const T_FOR                           = 320;
    public const T_ENDFOR                        = 321;
    public const T_FOREACH                       = 322;
    public const T_ENDFOREACH                    = 323;
    public const T_DECLARE                       = 324;
    public const T_ENDDECLARE                    = 325;
    public const T_AS                            = 326;
    public const T_SWITCH                        = 327;
    public const T_ENDSWITCH                     = 328;
    public const T_CASE                          = 329;
    public const T_DEFAULT                       = 330;
    public const T_BREAK                         = 331;
    public const T_CONTINUE                      = 332;
    public const T_GOTO                          = 333;
    public const T_FUNCTION                      = 334;
    public const T_CONST                         = 335;
    public const T_RETURN                        = 336;
    public const T_TRY                           = 337;
    public const T_CATCH                         = 338;
    public const T_THROW                         = 339;
    public const T_USE                           = 340;
    public const T_INSTEADOF                     = 341;
    public const T_GLOBAL                        = 342;
    public const T_PUBLIC                        = 343;
    public const T_PROTECTED                     = 344;
    public const T_PRIVATE                       = 345;
    public const T_FINAL                         = 346;
    public const T_ABSTRACT                      = 347;
    public const T_STATIC                        = 348;
    public const T_VAR                           = 349;
    public const T_UNSET                         = 350;
    public const T_ISSET                         = 351;
    public const T_EMPTY                         = 352;
    public const T_HALT_COMPILER                 = 353;
    public const T_CLASS                         = 354;
    public const T_TRAIT                         = 355;
    public const T_INTERFACE                     = 356;
    public const T_EXTENDS                       = 357;
    public const T_IMPLEMENTS                    = 358;
    public const T_OBJECT_OPERATOR               = 359;
    public const T_DOUBLE_ARROW                  = 360;
    public const T_LIST                          = 361;
    public const T_ARRAY                         = 362;
    public const T_CALLABLE                      = 363;
    public const T_CLASS_C                       = 364;
    public const T_TRAIT_C                       = 365;
    public const T_METHOD_C                      = 366;
    public const T_FUNC_C                        = 367;
    public const T_LINE                          = 368;
    public const T_FILE                          = 369;
    public const T_COMMENT                       = 370;
    public const T_DOC_COMMENT                   = 371;
    public const T_OPEN_TAG                      = 372;
    public const T_OPEN_TAG_WITH_ECHO            = 373;
    public const T_CLOSE_TAG                     = 374;
    public const T_WHITESPACE                    = 375;
    public const T_START_HEREDOC                 = 376;
    public const T_END_HEREDOC                   = 377;
    public const T_DOLLAR_OPEN_CURLY_BRACES      = 378;
    public const T_CURLY_OPEN                    = 379;
    public const T_PAAMAYIM_NEKUDOTAYIM          = 380;
    public const T_NAMESPACE                     = 381;
    public const T_NS_C                          = 382;
    public const T_DIR                           = 383;
    public const T_NS_SEPARATOR                  = 384;
    public const T_DOUBLE_COLON                  = 380;
    public const T_SPACESHIP                     = 1000;
    public const T_YIELD_FROM                    = 1000;
    public const T_COALESCE                      = 1000;
    public const T_COALESCE_EQUAL                = 1000;
    public const T_FN                            = 1000;
    public const T_POW_EQUAL                     = 1000;
    public const T_POW                           = 1000;
    public const T_ELLIPSIS                      = 1000;
    public const T_NAME_FULLY_QUALIFIED          = 1000;
    public const T_NAME_RELATIVE                 = 1000;
    public const T_NAME_QUALIFIED                = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR      = 1000;
    public const T_MATCH                         = 1000;
    public const T_ATTRIBUTE                     = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Strval extends Plugin {
    public const NO_VALUE = null;

    private $skipAtoms = array('Trait'         => 1,
                              'Class'          => 1,
                              'Classanonymous' => 1,
                              'Interface'      => 1,
                             );

    public $name = 'noDelimiter';
    public $type = 'string';

    public function run(Atom $atom, array $extras): void {
        if (isset($this->skipAtoms[$atom->atom])) {
            return;
        }

        // Ignoring $extras['LEFT'] === null
        if ($atom->atom === 'Assignation') {
            if ($atom->code === '=') {
                $atom->noDelimiter =  $extras['RIGHT']->noDelimiter;
            }

            return;
        }

        foreach($extras as $extra) {
            if (is_array($extra)) { continue; }
            if ($extra->noDelimiter === self::NO_VALUE)  {
                $atom->noDelimiter = self::NO_VALUE;
                return ;
            }
        }

        switch ($atom->atom) {
            case 'Integer' :
                $value = (string) $atom->code;
                // remove the digit separator
                $value = str_replace('_', '', $value);

                if (strtolower(substr($value, 0, 2)) === '0b') {
                    $actual = bindec(substr($value, 2));
                } elseif (strtolower(substr($value, 0, 2)) === '0x') {
                    $actual = hexdec(substr($value, 2));
                } elseif (strtolower(substr($value, 0, 2)) === '0o') {
                    $actual = octdec(substr($value, 2));
                } elseif (strtolower($value[0]) === '0') {
                    // PHP 7 will just stop.
                    // PHP 5 will work until it fails
                    $actual = octdec(substr($value, 1));
                } elseif ($value[0] === '+' || $value[0] === '-') {
                    $actual = (string) (int) pow(-1, substr_count($value, '-')) * (int) strtr($value, '+-', '  ');
                } else {
                    $actual = (string) (int) $value;
                }

                $atom->noDelimiter = $actual;
                break;

            case 'Float' :
            case 'String' :
            case 'Identifier': // Nsname creates a fatal error
                if (empty($extras)) {
                    $atom->noDelimiter = trimOnce($atom->code);
                } else {
                    $fullcodes = array_column($extras, 'fullcode');
                    $atom->noDelimiter = implode('', $fullcodes);
                }
                break;

            case 'Constant' :
                $atom->noDelimiter = $extras['VALUE']->noDelimiter;
                break;

            case 'Boolean' :
                $atom->noDelimiter = (string) (mb_strtolower($atom->code) === 'true');
                break;

            case 'Null' :
            case 'Void' :
                $atom->noDelimiter = '';
                break;

            case 'Staticclass' :
                $atom->noDelimiter = $atom->fullcode;
                break;

            case 'Parenthesis' :
                $atom->noDelimiter = $extras['CODE']->noDelimiter ?? '';
                break;

            case 'Addition' :
                if ($atom->code === '+') {
                    $atom->noDelimiter = (int) $extras['LEFT']->noDelimiter +
                                         (int) $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '-') {
                    $atom->noDelimiter = (int) $extras['LEFT']->noDelimiter -
                                         (int) $extras['RIGHT']->noDelimiter;
                }
                break;

            case 'Multiplication' :
                if ($atom->code === '*') {
                    $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter * (int) $extras['RIGHT']->noDelimiter);
                } elseif ($atom->code === '/' && (int) $extras['RIGHT']->noDelimiter != 0) {
                    $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter / (int) $extras['RIGHT']->noDelimiter);
                } elseif ($atom->code === '%' && (int) $extras['RIGHT']->noDelimiter != 0) {
                    $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter % (int) $extras['RIGHT']->noDelimiter);
                }
                break;

            case 'Power' :
                $atom->noDelimiter = ((int) $extras['LEFT']->noDelimiter) ** (int) $extras['RIGHT']->noDelimiter;
                if (is_nan($atom->noDelimiter) || is_infinite($atom->noDelimiter)) {
                    $atom->noDelimiter = '';
                }
                break;

            case 'Arrayliteral' :
                $atom->noDelimiter    = 'Array';
                break;

            case 'Not' :
                if ($atom->code === '!') {
                    $atom->noDelimiter = !$extras['NOT']->noDelimiter;
                } elseif ($atom->code === '~') {
                    $atom->noDelimiter = ~$extras['NOT']->noDelimiter;
                }
                break;

            case 'Bitwise' :
                if ($atom->code === '|') {
                    if (is_string($extras['LEFT']->noDelimiter) && is_string($extras['RIGHT']->noDelimiter)) {
                        $atom->noDelimiter = $extras['LEFT']->noDelimiter | $extras['RIGHT']->noDelimiter;
                    } else {
                        $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter | (int) $extras['RIGHT']->noDelimiter);
                    }
                } elseif ($atom->code === '&') {
                    if (is_string($extras['LEFT']->noDelimiter) && is_string($extras['RIGHT']->noDelimiter)) {
                        $atom->noDelimiter = $extras['LEFT']->noDelimiter & $extras['RIGHT']->noDelimiter;
                    } else {
                        $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter & (int) $extras['RIGHT']->noDelimiter);
                    }
                } elseif ($atom->code === '^') {
                    if (is_string($extras['LEFT']->noDelimiter) && is_string($extras['RIGHT']->noDelimiter)) {
                        $atom->noDelimiter = $extras['LEFT']->noDelimiter ^ $extras['RIGHT']->noDelimiter;
                    } else {
                        $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter ^ (int) $extras['RIGHT']->noDelimiter);
                    }
                }
                break;

            case 'Spaceship' :
                $atom->noDelimiter = (string) ((int) $extras['LEFT']->noDelimiter <=> (int) $extras['RIGHT']->noDelimiter);
                break;

            case 'Logical' :
                if ($atom->code === '&&' || mb_strtolower($atom->code) === 'and') {
                    $atom->noDelimiter = (int) $extras['LEFT']->noDelimiter && (int) $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '||' || mb_strtolower($atom->code) === 'or') {
                    $atom->noDelimiter = (int) $extras['LEFT']->noDelimiter && (int) $extras['RIGHT']->noDelimiter;
                } elseif (mb_strtolower($atom->code) === 'xor') {
                    $atom->noDelimiter = ((int) $extras['LEFT']->noDelimiter xor (int) $extras['RIGHT']->noDelimiter);
                }
                break;

            case 'Heredoc' :
                $noDelimiters = array_column($extras, 'noDelimiter');
                $atom->noDelimiter = rtrim(implode('', $noDelimiters));
                break;

            case 'Concatenation' :
                $noDelimiters = array_column($extras, 'noDelimiter');
                $atom->noDelimiter = implode('', $noDelimiters);
                break;

            case 'Ternary' :
                if ($extras['CONDITION']->noDelimiter) {
                    $atom->noDelimiter = $extras['THEN']->noDelimiter;
                } else {
                    $atom->noDelimiter = $extras['ELSE']->noDelimiter;
                }
                break;

            case 'Coalesce' :
                if ($extras['LEFT']->noDelimiter) {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter;
                } else {
                    $atom->noDelimiter = $extras['RIGHT']->noDelimiter;
                }
                break;

            case 'Bitshift' :
                if ((int) $extras['RIGHT']->noDelimiter <= 0) {
                    // This would generate an error
                    $atom->noDelimiter = '';
                } elseif ($atom->code === '>>') {
                    $atom->noDelimiter = (int) $extras['LEFT']->noDelimiter >> (int) $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '<<') {
                    $atom->noDelimiter = (int) $extras['LEFT']->noDelimiter << (int) $extras['RIGHT']->noDelimiter;
                }
                break;

            case 'Comparison' :
                if ($atom->code === '==') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter == $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '===') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter === $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '!=' || $atom->code === '<>') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter != $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '!==') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter !== $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '>') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter > $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '<') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter < $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '>=') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter >= $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '<=') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter <= $extras['RIGHT']->noDelimiter;
                } elseif ($atom->code === '<=>') {
                    $atom->noDelimiter = $extras['LEFT']->noDelimiter <=> $extras['RIGHT']->noDelimiter;
                }
                break;

            case 'Functioncall' :
            case 'Self' :
            case 'Parent' :
                $atom->noDelimiter = null;
                break;

            case 'Magicconstant' :
                $atom->noDelimiter = $atom->fullcode;
                break;

        default :
            // Nothing, really
        }
    }
}

?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class Php81 extends Php {

    // PHP tokens
    public const T_LNUMBER                                          = 260;
    public const T_DNUMBER                                          = 261;
    public const T_STRING                                           = 262;
    public const T_NAME_FULLY_QUALIFIED                             = 263;
    public const T_NAME_RELATIVE                                    = 264;
    public const T_NAME_QUALIFIED                                   = 265;
    public const T_VARIABLE                                         = 266;
    public const T_INLINE_HTML                                      = 267;
    public const T_ENCAPSED_AND_WHITESPACE                          = 268;
    public const T_CONSTANT_ENCAPSED_STRING                         = 269;
    public const T_STRING_VARNAME                                   = 270;
    public const T_NUM_STRING                                       = 271;
    public const T_INCLUDE                                          = 272;
    public const T_INCLUDE_ONCE                                     = 273;
    public const T_EVAL                                             = 274;
    public const T_REQUIRE                                          = 275;
    public const T_REQUIRE_ONCE                                     = 276;
    public const T_LOGICAL_OR                                       = 277;
    public const T_LOGICAL_XOR                                      = 278;
    public const T_LOGICAL_AND                                      = 279;
    public const T_PRINT                                            = 280;
    public const T_YIELD                                            = 281;
    public const T_YIELD_FROM                                       = 282;
    public const T_INSTANCEOF                                       = 283;
    public const T_NEW                                              = 284;
    public const T_CLONE                                            = 285;
    public const T_EXIT                                             = 286;
    public const T_IF                                               = 287;
    public const T_ELSEIF                                           = 288;
    public const T_ELSE                                             = 289;
    public const T_ENDIF                                            = 290;
    public const T_ECHO                                             = 291;
    public const T_DO                                               = 292;
    public const T_WHILE                                            = 293;
    public const T_ENDWHILE                                         = 294;
    public const T_FOR                                              = 295;
    public const T_ENDFOR                                           = 296;
    public const T_FOREACH                                          = 297;
    public const T_ENDFOREACH                                       = 298;
    public const T_DECLARE                                          = 299;
    public const T_ENDDECLARE                                       = 300;
    public const T_AS                                               = 301;
    public const T_SWITCH                                           = 302;
    public const T_ENDSWITCH                                        = 303;
    public const T_CASE                                             = 304;
    public const T_DEFAULT                                          = 305;
    public const T_MATCH                                            = 306;
    public const T_BREAK                                            = 307;
    public const T_CONTINUE                                         = 308;
    public const T_GOTO                                             = 309;
    public const T_FUNCTION                                         = 310;
    public const T_FN                                               = 311;
    public const T_CONST                                            = 312;
    public const T_RETURN                                           = 313;
    public const T_TRY                                              = 314;
    public const T_CATCH                                            = 315;
    public const T_FINALLY                                          = 316;
    public const T_THROW                                            = 317;
    public const T_USE                                              = 318;
    public const T_INSTEADOF                                        = 319;
    public const T_GLOBAL                                           = 320;
    public const T_STATIC                                           = 321;
    public const T_ABSTRACT                                         = 322;
    public const T_FINAL                                            = 323;
    public const T_PRIVATE                                          = 324;
    public const T_PROTECTED                                        = 325;
    public const T_PUBLIC                                           = 326;
    public const T_READONLY                                         = 327;
    public const T_VAR                                              = 328;
    public const T_UNSET                                            = 329;
    public const T_ISSET                                            = 330;
    public const T_EMPTY                                            = 331;
    public const T_HALT_COMPILER                                    = 332;
    public const T_CLASS                                            = 333;
    public const T_TRAIT                                            = 334;
    public const T_INTERFACE                                        = 335;
    public const T_ENUM                                             = 336;
    public const T_EXTENDS                                          = 337;
    public const T_IMPLEMENTS                                       = 338;
    public const T_NAMESPACE                                        = 339;
    public const T_LIST                                             = 340;
    public const T_ARRAY                                            = 341;
    public const T_CALLABLE                                         = 342;
    public const T_LINE                                             = 343;
    public const T_FILE                                             = 344;
    public const T_DIR                                              = 345;
    public const T_CLASS_C                                          = 346;
    public const T_TRAIT_C                                          = 347;
    public const T_METHOD_C                                         = 348;
    public const T_FUNC_C                                           = 349;
    public const T_NS_C                                             = 350;
    public const T_ATTRIBUTE                                        = 351;
    public const T_PLUS_EQUAL                                       = 352;
    public const T_MINUS_EQUAL                                      = 353;
    public const T_MUL_EQUAL                                        = 354;
    public const T_DIV_EQUAL                                        = 355;
    public const T_CONCAT_EQUAL                                     = 356;
    public const T_MOD_EQUAL                                        = 357;
    public const T_AND_EQUAL                                        = 358;
    public const T_OR_EQUAL                                         = 359;
    public const T_XOR_EQUAL                                        = 360;
    public const T_SL_EQUAL                                         = 361;
    public const T_SR_EQUAL                                         = 362;
    public const T_COALESCE_EQUAL                                   = 363;
    public const T_BOOLEAN_OR                                       = 364;
    public const T_BOOLEAN_AND                                      = 365;
    public const T_IS_EQUAL                                         = 366;
    public const T_IS_NOT_EQUAL                                     = 367;
    public const T_IS_IDENTICAL                                     = 368;
    public const T_IS_NOT_IDENTICAL                                 = 369;
    public const T_IS_SMALLER_OR_EQUAL                              = 370;
    public const T_IS_GREATER_OR_EQUAL                              = 371;
    public const T_SPACESHIP                                        = 372;
    public const T_SL                                               = 373;
    public const T_SR                                               = 374;
    public const T_INC                                              = 375;
    public const T_DEC                                              = 376;
    public const T_INT_CAST                                         = 377;
    public const T_DOUBLE_CAST                                      = 378;
    public const T_STRING_CAST                                      = 379;
    public const T_ARRAY_CAST                                       = 380;
    public const T_OBJECT_CAST                                      = 381;
    public const T_BOOL_CAST                                        = 382;
    public const T_UNSET_CAST                                       = 383;
    public const T_OBJECT_OPERATOR                                  = 384;
    public const T_NULLSAFE_OBJECT_OPERATOR                         = 385;
    public const T_DOUBLE_ARROW                                     = 386;
    public const T_COMMENT                                          = 387;
    public const T_DOC_COMMENT                                      = 388;
    public const T_OPEN_TAG                                         = 389;
    public const T_OPEN_TAG_WITH_ECHO                               = 390;
    public const T_CLOSE_TAG                                        = 391;
    public const T_WHITESPACE                                       = 392;
    public const T_START_HEREDOC                                    = 393;
    public const T_END_HEREDOC                                      = 394;
    public const T_DOLLAR_OPEN_CURLY_BRACES                         = 395;
    public const T_CURLY_OPEN                                       = 396;
    public const T_PAAMAYIM_NEKUDOTAYIM                             = 397;
    public const T_NS_SEPARATOR                                     = 398;
    public const T_ELLIPSIS                                         = 399;
    public const T_COALESCE                                         = 400;
    public const T_POW                                              = 401;
    public const T_POW_EQUAL                                        = 402;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 403;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 404;
    public const T_BAD_CHARACTER                                    = 405;
    public const T_DOUBLE_COLON                                     = 397;
    public const T_CHARACTER                                        = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2021 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;


class Php56 extends Php {

    // PHP tokens
    public const T_REQUIRE_ONCE                                     = 262;
    public const T_REQUIRE                                          = 261;
    public const T_EVAL                                             = 260;
    public const T_INCLUDE_ONCE                                     = 259;
    public const T_INCLUDE                                          = 258;
    public const T_LOGICAL_OR                                       = 263;
    public const T_LOGICAL_XOR                                      = 264;
    public const T_LOGICAL_AND                                      = 265;
    public const T_PRINT                                            = 266;
    public const T_YIELD                                            = 267;
    public const T_POW_EQUAL                                        = 389;
    public const T_SR_EQUAL                                         = 278;
    public const T_SL_EQUAL                                         = 277;
    public const T_XOR_EQUAL                                        = 276;
    public const T_OR_EQUAL                                         = 275;
    public const T_AND_EQUAL                                        = 274;
    public const T_MOD_EQUAL                                        = 273;
    public const T_CONCAT_EQUAL                                     = 272;
    public const T_DIV_EQUAL                                        = 271;
    public const T_MUL_EQUAL                                        = 270;
    public const T_MINUS_EQUAL                                      = 269;
    public const T_PLUS_EQUAL                                       = 268;
    public const T_BOOLEAN_OR                                       = 279;
    public const T_BOOLEAN_AND                                      = 280;
    public const T_IS_NOT_IDENTICAL                                 = 284;
    public const T_IS_IDENTICAL                                     = 283;
    public const T_IS_NOT_EQUAL                                     = 282;
    public const T_IS_EQUAL                                         = 281;
    public const T_IS_GREATER_OR_EQUAL                              = 286;
    public const T_IS_SMALLER_OR_EQUAL                              = 285;
    public const T_SR                                               = 288;
    public const T_SL                                               = 287;
    public const T_INSTANCEOF                                       = 289;
    public const T_UNSET_CAST                                       = 298;
    public const T_BOOL_CAST                                        = 297;
    public const T_OBJECT_CAST                                      = 296;
    public const T_ARRAY_CAST                                       = 295;
    public const T_STRING_CAST                                      = 294;
    public const T_DOUBLE_CAST                                      = 293;
    public const T_INT_CAST                                         = 292;
    public const T_DEC                                              = 291;
    public const T_INC                                              = 290;
    public const T_POW                                              = 388;
    public const T_CLONE                                            = 300;
    public const T_NEW                                              = 299;
    public const T_EXIT                                             = 301;
    public const T_IF                                               = 302;
    public const T_ELSEIF                                           = 303;
    public const T_ELSE                                             = 304;
    public const T_ENDIF                                            = 305;
    public const T_LNUMBER                                          = 306;
    public const T_DNUMBER                                          = 307;
    public const T_STRING                                           = 308;
    public const T_STRING_VARNAME                                   = 309;
    public const T_VARIABLE                                         = 310;
    public const T_NUM_STRING                                       = 311;
    public const T_INLINE_HTML                                      = 312;
    public const T_CHARACTER                                        = 313;
    public const T_BAD_CHARACTER                                    = 314;
    public const T_ENCAPSED_AND_WHITESPACE                          = 315;
    public const T_CONSTANT_ENCAPSED_STRING                         = 316;
    public const T_ECHO                                             = 317;
    public const T_DO                                               = 318;
    public const T_WHILE                                            = 319;
    public const T_ENDWHILE                                         = 320;
    public const T_FOR                                              = 321;
    public const T_ENDFOR                                           = 322;
    public const T_FOREACH                                          = 323;
    public const T_ENDFOREACH                                       = 324;
    public const T_DECLARE                                          = 325;
    public const T_ENDDECLARE                                       = 326;
    public const T_AS                                               = 327;
    public const T_SWITCH                                           = 328;
    public const T_ENDSWITCH                                        = 329;
    public const T_CASE                                             = 330;
    public const T_DEFAULT                                          = 331;
    public const T_BREAK                                            = 332;
    public const T_CONTINUE                                         = 333;
    public const T_GOTO                                             = 334;
    public const T_FUNCTION                                         = 335;
    public const T_CONST                                            = 336;
    public const T_RETURN                                           = 337;
    public const T_TRY                                              = 338;
    public const T_CATCH                                            = 339;
    public const T_FINALLY                                          = 340;
    public const T_THROW                                            = 341;
    public const T_USE                                              = 342;
    public const T_INSTEADOF                                        = 343;
    public const T_GLOBAL                                           = 344;
    public const T_PUBLIC                                           = 350;
    public const T_PROTECTED                                        = 349;
    public const T_PRIVATE                                          = 348;
    public const T_FINAL                                            = 347;
    public const T_ABSTRACT                                         = 346;
    public const T_STATIC                                           = 345;
    public const T_VAR                                              = 351;
    public const T_UNSET                                            = 352;
    public const T_ISSET                                            = 353;
    public const T_EMPTY                                            = 354;
    public const T_HALT_COMPILER                                    = 355;
    public const T_CLASS                                            = 356;
    public const T_TRAIT                                            = 357;
    public const T_INTERFACE                                        = 358;
    public const T_EXTENDS                                          = 359;
    public const T_IMPLEMENTS                                       = 360;
    public const T_OBJECT_OPERATOR                                  = 361;
    public const T_DOUBLE_ARROW                                     = 362;
    public const T_LIST                                             = 363;
    public const T_ARRAY                                            = 364;
    public const T_CALLABLE                                         = 365;
    public const T_CLASS_C                                          = 366;
    public const T_TRAIT_C                                          = 367;
    public const T_METHOD_C                                         = 368;
    public const T_FUNC_C                                           = 369;
    public const T_LINE                                             = 370;
    public const T_FILE                                             = 371;
    public const T_COMMENT                                          = 372;
    public const T_DOC_COMMENT                                      = 373;
    public const T_OPEN_TAG                                         = 374;
    public const T_OPEN_TAG_WITH_ECHO                               = 375;
    public const T_CLOSE_TAG                                        = 376;
    public const T_WHITESPACE                                       = 377;
    public const T_START_HEREDOC                                    = 378;
    public const T_END_HEREDOC                                      = 379;
    public const T_DOLLAR_OPEN_CURLY_BRACES                         = 380;
    public const T_CURLY_OPEN                                       = 381;
    public const T_PAAMAYIM_NEKUDOTAYIM                             = 382;
    public const T_NAMESPACE                                        = 383;
    public const T_NS_C                                             = 384;
    public const T_DIR                                              = 385;
    public const T_NS_SEPARATOR                                     = 386;
    public const T_ELLIPSIS                                         = 387;
    public const T_DOUBLE_COLON                                     = 382;
    public const T_SPACESHIP                                        = 1000;
    public const T_YIELD_FROM                                       = 1000;
    public const T_COALESCE                                         = 1000;
    public const T_COALESCE_EQUAL                                   = 1000;
    public const T_FN                                               = 1000;
    public const T_NAME_FULLY_QUALIFIED                             = 1000;
    public const T_NAME_RELATIVE                                    = 1000;
    public const T_NAME_QUALIFIED                                   = 1000;
    public const T_NULLSAFE_OBJECT_OPERATOR                         = 1000;
    public const T_MATCH                                            = 1000;
    public const T_ATTRIBUTE                                        = 1000;
    public const T_ENUM                                             = 1000;
    public const T_READONLY                                         = 1000;
    public const T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG              = 1000;
    public const T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG          = 1000;
}
?>
<?php declare(strict_types = 1);
/*
 * Copyright 2012-2022 Damien Seguy – Exakat SAS <contact(at)exakat.io>
 * This file is part of Exakat.
 *
 * Exakat 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.
 *
 * Exakat 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 Exakat.  If not, see <http://www.gnu.org/licenses/>.
 *
 * The latest code can be found at <http://exakat.io/>.
 *
*/

namespace Exakat\Tasks\Helpers;

class Intval extends Plugin {
    public const NO_VALUE = '';

    public $name = 'intval';
    public $type = 'integer';

    private $skipAtoms = array('Trait'         => 1,
                              'Class'          => 1,
                              'Classanonymous' => 1,
                              'Interface'      => 1,
                             );

    public function run(Atom $atom, array $extras): void {
        if (isset($this->skipAtoms[$atom->atom])) {
            return;
        }

        // Ignoring $extras['LEFT'] === null
        if ($atom->atom === 'Assignation') {
            if ($atom->code === '=') {
                $atom->intval =  $extras['RIGHT']->intval;
            }

            return;
        }

        foreach($extras as $extra) {
            if (is_array($extra)) { continue;}
            if ($extra->intval === self::NO_VALUE)  {
                $atom->intval = self::NO_VALUE;
                return ;
            }
        }

        switch ($atom->atom) {
            case 'Integer' :
                $value = (string) $atom->code;
                // remove the digit separator
                $value = str_replace('_', '', $value);

                if (strtolower(substr($value, 0, 2)) === '0b') {
                    $actual = bindec(substr($value, 2));
                } elseif (strtolower(substr($value, 0, 2)) === '0x') {
                    $actual = hexdec(substr($value, 2));
                } elseif (strtolower(substr($value, 0, 2)) === '0o') { // PHP 8.1
                    $actual = octdec(substr($value, 2));
                } elseif (strtolower($value[0]) === '0') {
                    // PHP 7 will just stop.
                    // PHP 5 will work until it fails
                    $actual = octdec(substr($value, 1));
                } elseif ($value[0] === '+' || $value[0] === '-') {
                    $actual = (int) pow(-1, substr_count($value, '-')) * (int) strtr($value, '+-', '  ');
                } else {
                    $actual = (int) $value;
                }

                $atom->intval = $actual == PHP_INT_MIN ? 0 : $actual;
                break;

            case 'Float' :
            case 'String' :
            case 'Heredoc' :
                if (empty($extras)) {
                    $atom->intval   = (int) trimOnce($atom->code);
                } else {
                    $atom->intval   = (int) array_sum(array_column($extras, 'intval'));
                }
                break;

            case 'Boolean' :
                $atom->intval = (int) (mb_strtolower(trim($atom->code, '\\')) === 'true');
                break;

            case 'Staticclass' :
            case 'Identifier'  :
//            case 'Nsname'      : This leads to a fatal error
            case 'Self'        :
            case 'Parent'      :
            case 'Magicconstant' :
                $atom->intval = self::NO_VALUE;
                break;

            case 'Null'        :
            case 'Void'        :
                $atom->intval = 0;
                break;

            case 'Parenthesis' :
                $atom->intval = $extras['CODE']->intval ?? 0;
                break;

            case 'Addition' :
                if ($atom->code === '+') {
                    $atom->intval = $extras['LEFT']->intval + $extras['RIGHT']->intval;
                } elseif ($atom->code === '-') {
                    $atom->intval = $extras['LEFT']->intval - $extras['RIGHT']->intval;
                }
                break;

            case 'Multiplication' :
                if ($atom->code === '*') {
                    $atom->intval = (int) ($extras['LEFT']->intval * $extras['RIGHT']->intval);
                } elseif ($atom->code === '/') {
                    if ((int) $extras['RIGHT']->intval === 0) {
                        $atom->intval = 0;
                    } else {
                        $atom->intval = intdiv((int) $extras['LEFT']->intval , (int) $extras['RIGHT']->intval);
                    }
                } elseif ($atom->code === '%') {
                    if ((int) $extras['RIGHT']->intval === 0) {
                        $atom->intval = 0;
                    } else {
                        $atom->intval = ($extras['LEFT']->intval % $extras['RIGHT']->intval);
                    }
                }
                break;

            case 'Power' :
                $atom->intval = ((int) $extras['LEFT']->intval) ** (int) $extras['RIGHT']->intval;
                if (is_nan($atom->intval) || is_infinite($atom->intval)) {
                    $atom->intval = 0;
                }
                break;

            case 'Arrayliteral' :
                $atom->intval    = (int) (bool) $atom->count;
                break;

            case 'Constant' :
                $atom->intval    = $extras['VALUE']->intval;
                break;

            case 'Not' :
                if ($atom->code === '!') {
                    $atom->intval = !$extras['NOT']->intval;
                } elseif ($atom->code === '~') {
                    $atom->intval = ~$extras['NOT']->intval;
                }
                break;

            case 'Bitwise' :
                if ($atom->code === '|') {
                    $atom->intval = $extras['LEFT']->intval | $extras['RIGHT']->intval;
                } elseif ($atom->code === '&') {
                    $atom->intval = $extras['LEFT']->intval & $extras['RIGHT']->intval;
                } elseif ($atom->code === '^') {
                    $atom->intval = $extras['LEFT']->intval ^ $extras['RIGHT']->intval;
                }
                break;

            case 'Spaceship' :
                $atom->intval = $extras['LEFT']->intval <=> $extras['RIGHT']->intval;
                break;

            case 'Logical' :
                if ($atom->code === '&&' || mb_strtolower($atom->code) === 'and') {
                    $atom->intval = $extras['LEFT']->intval && $extras['RIGHT']->intval;
                } elseif ($atom->code === '||' || mb_strtolower($atom->code) === 'or') {
                    $atom->intval = $extras['LEFT']->intval && $extras['RIGHT']->intval;
                } elseif (mb_strtolower($atom->code) === 'xor') {
                    $atom->intval = ($extras['LEFT']->intval xor $extras['RIGHT']->intval);
                }
                break;

            case 'Concatenation' :
                $intval = array_column($extras, 'noDelimiter');
                $atom->intval = (int) implode('', $intval);
                break;

            case 'Ternary' :
                if ($extras['CONDITION']->intval) {
                    $atom->intval = (int) $extras['THEN']->intval;
                } else {
                    $atom->intval = (int) $extras['ELSE']->intval;
                }
                break;

            case 'Coalesce' :
                if ($extras['LEFT']->intval) {
                    $atom->intval = (int) $extras['LEFT']->intval;
                } else {
                    $atom->intval = (int) $extras['RIGHT']->intval;
                }
                break;

            case 'Bitshift' :
                if ($extras['RIGHT']->intval <= 0) {
                    // This would generate an error anyway
                    $atom->intva