phpdoc.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. <?php
  2. /**
  3. * This file is part of the Carbon package.
  4. *
  5. * (c) Brian Nesbitt <brian@nesbot.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. use Carbon\Carbon;
  11. use Carbon\CarbonImmutable;
  12. use Carbon\CarbonInterface;
  13. use Carbon\Factory;
  14. use Carbon\FactoryImmutable;
  15. use Symfony\Component\Translation\TranslatorInterface;
  16. $tags = [
  17. 'property',
  18. 'property-read',
  19. PHP_EOL,
  20. 'mode',
  21. ['call', 'is'],
  22. ['call', 'isDayOfWeek'],
  23. ['call', 'isSameUnit'],
  24. ['call', 'setUnit'],
  25. ['call', 'addUnit'],
  26. ['call', 'addRealUnit'],
  27. ['call', 'roundUnit'],
  28. ['call', 'diffForHumans'],
  29. ];
  30. $nativeMethods = [
  31. 'getOffset' => 'int',
  32. 'getTimestamp' => 'int',
  33. ];
  34. $modes = [];
  35. $autoDocLines = [];
  36. $carbon = __DIR__.'/src/Carbon/Carbon.php';
  37. $immutable = __DIR__.'/src/Carbon/CarbonImmutable.php';
  38. $interface = __DIR__.'/src/Carbon/CarbonInterface.php';
  39. $phpLevel = 7.1;
  40. file_put_contents($interface, preg_replace('/(\/\/ <methods[\s\S]*>)([\s\S]+)(<\/methods>)/mU', "$1\n\n // $3", file_get_contents($interface), 1));
  41. require_once __DIR__.'/vendor/autoload.php';
  42. $trait = __DIR__.'/src/Carbon/Traits/Date.php';
  43. $code = '';
  44. $overrideTyping = [
  45. $carbon => [
  46. // 'createFromImmutable' => ['static Carbon', 'DateTimeImmutable $dateTime', 'Create a new Carbon object from an immutable date.'],
  47. 'createFromFormat' => ['static static|false', 'string $format, string $time, DateTimeZone|string|false|null $timezone = null', 'Parse a string into a new Carbon object according to the specified format.'],
  48. '__set_state' => ['static static', 'array $array', 'https://php.net/manual/en/datetime.set-state.php'],
  49. ],
  50. $immutable => [
  51. // 'createFromMutable' => ['static CarbonImmutable', 'DateTime $dateTime', 'Create a new CarbonImmutable object from an immutable date.'],
  52. 'createFromFormat' => ['static static|false', 'string $format, string $time, DateTimeZone|string|false|null $timezone = null', 'Parse a string into a new CarbonImmutable object according to the specified format.'],
  53. '__set_state' => ['static static', 'array $array', 'https://php.net/manual/en/datetime.set-state.php'],
  54. ],
  55. ];
  56. foreach (glob(__DIR__.'/src/Carbon/Traits/*.php') as $file) {
  57. $code .= file_get_contents($file);
  58. }
  59. function unitName($unit)
  60. {
  61. switch ($unit) {
  62. case 'milli':
  63. return 'millisecond';
  64. case 'micro':
  65. return 'microsecond';
  66. default:
  67. return $unit;
  68. }
  69. }
  70. function pluralize($word)
  71. {
  72. if (preg_match('/(millenni)um$/i', $word)) {
  73. return preg_replace('/(millenni)um$/i', '$1a', $word);
  74. }
  75. return preg_replace('/(centur)y$/i', '$1ie', $word).'s';
  76. }
  77. function dumpValue($value)
  78. {
  79. if ($value === null) {
  80. return 'null';
  81. }
  82. if ($value === CarbonInterface::TRANSLATE_ALL) {
  83. return 'CarbonInterface::TRANSLATE_ALL';
  84. }
  85. $value = preg_replace('/^array\s*\(\s*\)$/', '[]', var_export($value, true));
  86. $value = preg_replace('/^array\s*\(([\s\S]+)\)$/', '[$1]', $value);
  87. return $value;
  88. }
  89. function cleanClassName($name)
  90. {
  91. if (preg_match('/^[A-Z]/', $name)) {
  92. $name = "\\$name";
  93. }
  94. if (ltrim($name, '\\') === TranslatorInterface::class) {
  95. return 'TranslatorInterface';
  96. }
  97. return preg_replace('/^\\\\(Date(?:Time(?:Immutable|Interface|Zone)?|Interval)|[a-z]*Exception|Closure)$/i', '$1', preg_replace('/^\\\\Carbon\\\\/', '', $name));
  98. }
  99. function dumpParameter($method, ReflectionParameter $parameter)
  100. {
  101. global $defaultValues;
  102. $name = $parameter->getName();
  103. $output = '$'.$name;
  104. if ($parameter->isVariadic()) {
  105. $output = "...$output";
  106. }
  107. if ($parameter->getType()) {
  108. $name = cleanClassName($parameter->getType()->getName());
  109. if ($parameter->allowsNull()) {
  110. $name = "?$name";
  111. }
  112. $output = "$name $output";
  113. }
  114. if (isset($defaultValues[$method])) {
  115. if (isset($defaultValues[$method][$name])) {
  116. $output .= ' = '.dumpValue($defaultValues[$method][$name]);
  117. }
  118. return $output;
  119. }
  120. try {
  121. if ($parameter->isDefaultValueAvailable()) {
  122. $output .= ' = '.dumpValue($parameter->getDefaultValue());
  123. }
  124. } catch (ReflectionException $exp) {
  125. }
  126. return $output;
  127. }
  128. $deprecated = [];
  129. foreach ($tags as $tag) {
  130. if (\is_array($tag)) {
  131. [$tag, $pattern] = $tag;
  132. }
  133. $pattern = $pattern ?? '\S+';
  134. if ($tag === PHP_EOL) {
  135. $autoDocLines[] = '';
  136. continue;
  137. }
  138. preg_match_all('/\/\/ @'.$tag.'\s+(?<type>'.$pattern.')(?:\s+\$(?<name>\S+)(?:[^\S\n](?<description>.*))?\n|(?:[^\S\n](?<description2>.*))?\n(?<comments>(?:[ \t]+\/\/[^\n]*\n)*)[^\']*\'(?<name2>[^\']+)\')/', $code, $matches, PREG_SET_ORDER);
  139. foreach ($matches as $match) {
  140. $vars = (object) $match;
  141. $deprecation = null;
  142. if (isset($vars->comments) && preg_match(
  143. '`^[ \t]+(//|/*|#)[ \t]*@deprecated(?:\s(?<deprecation>[\s\S]*))?$`',
  144. $vars->comments,
  145. $comments
  146. )) {
  147. ['deprecation' => $deprecation] = $comments;
  148. $deprecation = preg_replace('/^\s*(\/\/|#|\*) {0,3}/m', '', trim($deprecation));
  149. if (preg_match('/^(?:[a-z]+:[^\n]+\n)+$/', "$deprecation\n")) {
  150. $data = [];
  151. foreach (explode("\n", $deprecation) as $line) {
  152. [$name, $value] = array_map('trim', explode(':', $line, 2));
  153. $data[$name] = $value;
  154. }
  155. $deprecation = $data;
  156. } else {
  157. $deprecation = ['reason' => $deprecation];
  158. }
  159. }
  160. $vars->name = $vars->name ?: $vars->name2;
  161. $vars->description = $vars->description ?: $vars->description2;
  162. if ($tag === 'mode') {
  163. if (!isset($modes[$vars->type])) {
  164. $modes[$vars->type] = [];
  165. }
  166. $modes[$vars->type][] = $vars->name;
  167. continue;
  168. }
  169. if ($tag === 'call') {
  170. switch ($vars->type) {
  171. case 'diffForHumans':
  172. foreach ($modes[$vars->type] as $mode) {
  173. $autoDocLines[] = [
  174. '@method',
  175. 'string',
  176. "$mode{$vars->name}DiffForHumans(DateTimeInterface \$other = null, int \$parts = 1)",
  177. "Get the difference ($mode format, '{$vars->name}' mode) in a human readable format in the current locale. (\$other and \$parts parameters can be swapped.)",
  178. ];
  179. }
  180. break;
  181. case 'isDayOfWeek':
  182. $autoDocLines[] = [
  183. '@method',
  184. 'bool',
  185. 'is'.ucFirst($vars->name).'()',
  186. 'Checks if the instance day is '.unitName(strtolower($vars->name)).'.',
  187. ];
  188. break;
  189. case 'is':
  190. $autoDocLines[] = [
  191. '@method',
  192. 'bool',
  193. 'is'.ucFirst($vars->name).'()',
  194. $vars->description,
  195. ];
  196. break;
  197. case 'isSameUnit':
  198. $unit = $vars->name;
  199. $unitName = unitName($unit);
  200. $method = 'isSame'.ucFirst($unit);
  201. if (!method_exists(Carbon::class, $method)) {
  202. $autoDocLines[] = [
  203. '@method',
  204. 'bool',
  205. $method.'(Carbon|DateTimeInterface|string|null $date = null)',
  206. "Checks if the given date is in the same $unitName as the instance. If null passed, compare to now (with the same timezone).",
  207. ];
  208. }
  209. $autoDocLines[] = [
  210. '@method',
  211. 'bool',
  212. 'isCurrent'.ucFirst($unit).'()',
  213. "Checks if the instance is in the same $unitName as the current moment.",
  214. ];
  215. $autoDocLines[] = [
  216. '@method',
  217. 'bool',
  218. 'isNext'.ucFirst($unit).'()',
  219. "Checks if the instance is in the same $unitName as the current moment next $unitName.",
  220. ];
  221. $autoDocLines[] = [
  222. '@method',
  223. 'bool',
  224. 'isLast'.ucFirst($unit).'()',
  225. "Checks if the instance is in the same $unitName as the current moment last $unitName.",
  226. ];
  227. break;
  228. case 'setUnit':
  229. $unit = $vars->name;
  230. $unitName = unitName($unit);
  231. $plUnit = pluralize($unit);
  232. $autoDocLines[] = [
  233. '@method',
  234. 'self',
  235. "$plUnit(int \$value)",
  236. "Set current instance $unitName to the given value.",
  237. ];
  238. $autoDocLines[] = [
  239. '@method',
  240. 'self',
  241. "$unit(int \$value)",
  242. "Set current instance $unitName to the given value.",
  243. ];
  244. $autoDocLines[] = [
  245. '@method',
  246. 'self',
  247. 'set'.ucfirst($plUnit).'(int $value)',
  248. "Set current instance $unitName to the given value.",
  249. ];
  250. $autoDocLines[] = [
  251. '@method',
  252. 'self',
  253. 'set'.ucfirst($unit).'(int $value)',
  254. "Set current instance $unitName to the given value.",
  255. ];
  256. break;
  257. case 'addUnit':
  258. $unit = $vars->name;
  259. $unitName = unitName($unit);
  260. $plUnit = pluralize($unit);
  261. $plUnitName = pluralize($unitName);
  262. $autoDocLines[] = [
  263. '@method',
  264. 'self',
  265. 'add'.ucFirst($plUnit).'(int $value = 1)',
  266. "Add $plUnitName (the \$value count passed in) to the instance (using date interval).",
  267. ];
  268. $autoDocLines[] = [
  269. '@method',
  270. 'self',
  271. 'add'.ucFirst($unit).'()',
  272. "Add one $unitName to the instance (using date interval).",
  273. ];
  274. $autoDocLines[] = [
  275. '@method',
  276. 'self',
  277. 'sub'.ucFirst($plUnit).'(int $value = 1)',
  278. "Sub $plUnitName (the \$value count passed in) to the instance (using date interval).",
  279. ];
  280. $autoDocLines[] = [
  281. '@method',
  282. 'self',
  283. 'sub'.ucFirst($unit).'()',
  284. "Sub one $unitName to the instance (using date interval).",
  285. ];
  286. if (\in_array($unit, [
  287. 'month',
  288. 'quarter',
  289. 'year',
  290. 'decade',
  291. 'century',
  292. 'millennium',
  293. ])) {
  294. $autoDocLines[] = [
  295. '@method',
  296. 'self',
  297. 'add'.ucFirst($plUnit).'WithOverflow(int $value = 1)',
  298. "Add $plUnitName (the \$value count passed in) to the instance (using date interval) with overflow explicitly allowed.",
  299. ];
  300. $autoDocLines[] = [
  301. '@method',
  302. 'self',
  303. 'add'.ucFirst($unit).'WithOverflow()',
  304. "Add one $unitName to the instance (using date interval) with overflow explicitly allowed.",
  305. ];
  306. $autoDocLines[] = [
  307. '@method',
  308. 'self',
  309. 'sub'.ucFirst($plUnit).'WithOverflow(int $value = 1)',
  310. "Sub $plUnitName (the \$value count passed in) to the instance (using date interval) with overflow explicitly allowed.",
  311. ];
  312. $autoDocLines[] = [
  313. '@method',
  314. 'self',
  315. 'sub'.ucFirst($unit).'WithOverflow()',
  316. "Sub one $unitName to the instance (using date interval) with overflow explicitly allowed.",
  317. ];
  318. foreach (['WithoutOverflow', 'WithNoOverflow', 'NoOverflow'] as $alias) {
  319. $autoDocLines[] = [
  320. '@method',
  321. 'self',
  322. 'add'.ucFirst($plUnit)."$alias(int \$value = 1)",
  323. "Add $plUnitName (the \$value count passed in) to the instance (using date interval) with overflow explicitly forbidden.",
  324. ];
  325. $autoDocLines[] = [
  326. '@method',
  327. 'self',
  328. 'add'.ucFirst($unit)."$alias()",
  329. "Add one $unitName to the instance (using date interval) with overflow explicitly forbidden.",
  330. ];
  331. $autoDocLines[] = [
  332. '@method',
  333. 'self',
  334. 'sub'.ucFirst($plUnit)."$alias(int \$value = 1)",
  335. "Sub $plUnitName (the \$value count passed in) to the instance (using date interval) with overflow explicitly forbidden.",
  336. ];
  337. $autoDocLines[] = [
  338. '@method',
  339. 'self',
  340. 'sub'.ucFirst($unit)."$alias()",
  341. "Sub one $unitName to the instance (using date interval) with overflow explicitly forbidden.",
  342. ];
  343. }
  344. break;
  345. }
  346. break;
  347. case 'addRealUnit':
  348. $unit = $vars->name;
  349. $unitName = unitName($unit);
  350. $plUnit = pluralize($unit);
  351. $plUnitName = pluralize($unitName);
  352. $autoDocLines[] = [
  353. '@method',
  354. 'self',
  355. 'addReal'.ucFirst($plUnit).'(int $value = 1)',
  356. "Add $plUnitName (the \$value count passed in) to the instance (using timestamp).",
  357. ];
  358. $autoDocLines[] = [
  359. '@method',
  360. 'self',
  361. 'addReal'.ucFirst($unit).'()',
  362. "Add one $unitName to the instance (using timestamp).",
  363. ];
  364. $autoDocLines[] = [
  365. '@method',
  366. 'self',
  367. 'subReal'.ucFirst($plUnit).'(int $value = 1)',
  368. "Sub $plUnitName (the \$value count passed in) to the instance (using timestamp).",
  369. ];
  370. $autoDocLines[] = [
  371. '@method',
  372. 'self',
  373. 'subReal'.ucFirst($unit).'()',
  374. "Sub one $unitName to the instance (using timestamp).",
  375. ];
  376. $autoDocLines[] = [
  377. '@method',
  378. 'CarbonPeriod',
  379. $plUnit.'Until($endDate = null, int $factor = 1)',
  380. "Return an iterable period from current date to given end (string, DateTime or Carbon instance) for each $unitName or every X $plUnitName if a factor is given.",
  381. ];
  382. break;
  383. case 'roundUnit':
  384. $unit = $vars->name;
  385. $unitName = unitName($unit);
  386. $plUnit = pluralize($unit);
  387. $autoDocLines[] = [
  388. '@method',
  389. 'self',
  390. 'round'.ucFirst($unit).'(float $precision = 1, string $function = "round")',
  391. "Round the current instance $unitName with given precision using the given function.",
  392. ];
  393. $autoDocLines[] = [
  394. '@method',
  395. 'self',
  396. 'round'.ucFirst($plUnit).'(float $precision = 1, string $function = "round")',
  397. "Round the current instance $unitName with given precision using the given function.",
  398. ];
  399. $autoDocLines[] = [
  400. '@method',
  401. 'self',
  402. 'floor'.ucFirst($unit).'(float $precision = 1)',
  403. "Truncate the current instance $unitName with given precision.",
  404. ];
  405. $autoDocLines[] = [
  406. '@method',
  407. 'self',
  408. 'floor'.ucFirst($plUnit).'(float $precision = 1)',
  409. "Truncate the current instance $unitName with given precision.",
  410. ];
  411. $autoDocLines[] = [
  412. '@method',
  413. 'self',
  414. 'ceil'.ucFirst($unit).'(float $precision = 1)',
  415. "Ceil the current instance $unitName with given precision.",
  416. ];
  417. $autoDocLines[] = [
  418. '@method',
  419. 'self',
  420. 'ceil'.ucFirst($plUnit).'(float $precision = 1)',
  421. "Ceil the current instance $unitName with given precision.",
  422. ];
  423. break;
  424. }
  425. continue;
  426. }
  427. $description = trim($vars->description);
  428. $variable = $vars->name;
  429. if (str_starts_with($description, '$')) {
  430. [$variable, $description] = explode(' ', $description, 2);
  431. $variable = ltrim($variable, '$');
  432. $description = ltrim($description);
  433. }
  434. if ($deprecation !== null) {
  435. $deprecated[$tag] = $deprecated[$tag] ?? [];
  436. $deprecated[$tag][] = [
  437. 'deprecation' => $deprecation,
  438. 'type' => $vars->type,
  439. 'variable' => $variable,
  440. 'description' => $description ?: '',
  441. ];
  442. continue;
  443. }
  444. $autoDocLines[] = [
  445. '@'.$tag,
  446. $vars->type,
  447. '$'.$variable,
  448. $description ?: '',
  449. ];
  450. }
  451. }
  452. $fileTemplate = <<<EOF
  453. <?php
  454. declare(strict_types=1);
  455. /**
  456. * This file is part of the Carbon package.
  457. *
  458. * (c) Brian Nesbitt <brian@nesbot.com>
  459. *
  460. * For the full copyright and license information, please view the LICENSE
  461. * file that was distributed with this source code.
  462. */
  463. namespace Carbon\Traits;
  464. trait DeprecatedProperties
  465. {/* content */}
  466. EOF;
  467. $propertyTemplate = '
  468. /**
  469. * %description%
  470. *
  471. * @var %type%
  472. *
  473. * @deprecated %line1%
  474. * %line2%
  475. */
  476. public $%variable%;
  477. ';
  478. $lineGlue = preg_replace('/^[\s\S]*%line1%([\s\S]*)%line2%[\s\S]*$/', '$1', $propertyTemplate);
  479. $propertyTemplate = preg_replace('/(%line1%[\s\S]*%line2%)/', '%deprecation%', $propertyTemplate);
  480. file_put_contents(
  481. __DIR__.'/src/Carbon/Traits/DeprecatedProperties.php',
  482. strtr($fileTemplate, [
  483. '/* content */' => implode('', array_map(static function (array $property) use ($lineGlue, $propertyTemplate) {
  484. if (isset($property['deprecation']['since'])) {
  485. $property['deprecation']['since'] = 'Deprecated since '.$property['deprecation']['since'];
  486. }
  487. return strtr($propertyTemplate, [
  488. '%description%' => $property['description'],
  489. '%type%' => $property['type'],
  490. '%variable%' => $property['variable'],
  491. '%deprecation%' => implode($lineGlue, array_filter([
  492. $property['deprecation']['reason'] ?? null,
  493. $property['deprecation']['replacement'] ?? null,
  494. $property['deprecation']['since'] ?? null,
  495. ])),
  496. ]);
  497. }, array_merge($deprecated['property'] ?? [], $deprecated['property-read'] ?? []))),
  498. ])
  499. );
  500. function compileDoc($autoDocLines, $file)
  501. {
  502. $class = 'CarbonInterface';
  503. if (preg_match('`[\\\\/](Carbon\w*)\.php$`', $file, $match)) {
  504. $class = $match[1];
  505. }
  506. $autoDoc = '';
  507. $columnsMaxLengths = [];
  508. foreach ($autoDocLines as &$editableLine) {
  509. if (\is_array($editableLine)) {
  510. if (($editableLine[1] ?? '') === 'self') {
  511. $editableLine[1] = $class === 'Carbon' ? '$this' : $class;
  512. }
  513. foreach ($editableLine as $column => $text) {
  514. $length = \strlen($text);
  515. $max = $columnsMaxLengths[$column] ?? 0;
  516. if ($length > $max) {
  517. $columnsMaxLengths[$column] = $length;
  518. }
  519. }
  520. }
  521. }
  522. foreach ($autoDocLines as $line) {
  523. $autoDoc .= "\n *";
  524. if (\is_string($line)) {
  525. if (!empty($line)) {
  526. $autoDoc .= " $line";
  527. }
  528. continue;
  529. }
  530. $computedLine = ' ';
  531. foreach ($line as $column => $text) {
  532. $computedLine .= str_pad($text, $columnsMaxLengths[$column] + 1, ' ', STR_PAD_RIGHT);
  533. }
  534. $autoDoc .= rtrim($computedLine);
  535. }
  536. return $autoDoc;
  537. }
  538. $files = new stdClass();
  539. foreach ([$trait, $carbon, $immutable, $interface] as $file) {
  540. $content = file_get_contents($file);
  541. $files->$file = preg_replace_callback('/(<autodoc[\s\S]*>)([\s\S]+)(<\/autodoc>)/mU', function ($matches) use ($file, $autoDocLines, $overrideTyping) {
  542. foreach (($overrideTyping[$file] ?? []) as $method => $line) {
  543. $line[1] = $method.'('.$line[1].')';
  544. array_unshift($line, '@method');
  545. $autoDocLines[] = $line;
  546. }
  547. $autoDoc = compileDoc($autoDocLines, $file);
  548. return $matches[1]."\n *$autoDoc\n *\n * ".$matches[3];
  549. }, $content, 1);
  550. }
  551. $staticMethods = [];
  552. $staticImmutableMethods = [];
  553. $methods = '';
  554. $carbonMethods = get_class_methods(Carbon::class);
  555. sort($carbonMethods);
  556. foreach ($carbonMethods as $method) {
  557. if (!method_exists(CarbonImmutable::class, $method) ||
  558. method_exists(DateTimeInterface::class, $method) ||
  559. $method === 'createFromInterface'
  560. ) {
  561. continue;
  562. }
  563. $function = new ReflectionMethod(Carbon::class, $method);
  564. $static = $function->isStatic() ? ' static' : '';
  565. $parameters = implode(', ', array_map(function (ReflectionParameter $parameter) use ($method) {
  566. return dumpParameter($method, $parameter);
  567. }, $function->getParameters()));
  568. $methodDocBlock = $function->getDocComment() ?: '';
  569. if (!str_starts_with($method, '__') && $function->isStatic()) {
  570. $doc = preg_replace('/^\/\*+\n([\s\S]+)\s*\*\//', '$1', $methodDocBlock);
  571. $doc = preg_replace('/^\s*\*\s?/m', '', $doc);
  572. $doc = explode("\n@", $doc, 2);
  573. $doc = preg_split('/(\r\n|\r|\n)/', trim($doc[0]));
  574. $returnType = $function->getReturnType();
  575. if ($returnType instanceof ReflectionNamedType) {
  576. $returnType = $returnType->getName();
  577. }
  578. if (!$returnType && preg_match('/\*\s*@returns?\s+(\S+)/', $methodDocBlock, $match)) {
  579. $returnType = $match[1];
  580. }
  581. $returnType = str_replace('static|CarbonInterface', 'static', $returnType ?: 'static');
  582. if (!method_exists(Factory::class, $method)) {
  583. $staticMethods[] = [
  584. '@method',
  585. str_replace('static', 'Carbon', $returnType),
  586. "$method($parameters)",
  587. $doc[0],
  588. ];
  589. for ($i = 1; $i < \count($doc); $i++) {
  590. $staticMethods[] = ['', '', '', $doc[$i]];
  591. }
  592. }
  593. if (!method_exists(FactoryImmutable::class, $method)) {
  594. $staticImmutableMethods[] = [
  595. '@method',
  596. str_replace('static', 'CarbonImmutable', $returnType),
  597. "$method($parameters)",
  598. $doc[0],
  599. ];
  600. for ($i = 1; $i < \count($doc); $i++) {
  601. $staticImmutableMethods[] = ['', '', '', $doc[$i]];
  602. }
  603. }
  604. }
  605. $return = $function->getReturnType() ? ': '.$function->getReturnType()->getName() : '';
  606. if (!empty($methodDocBlock)) {
  607. $methodDocBlock = "\n $methodDocBlock";
  608. } elseif (isset($nativeMethods[$method])) {
  609. $link = strtolower($method);
  610. $methodDocBlock = "\n /**\n".
  611. " * Calls DateTime::$method if mutable or DateTimeImmutable::$method else.\n".
  612. " *\n".
  613. " * @see https://php.net/manual/en/datetime.$link.php\n".
  614. ' */';
  615. }
  616. if (str_contains($return, 'self') && $phpLevel < 7.4) {
  617. $return = '';
  618. }
  619. if ($method === '__toString' && $phpLevel < 8) {
  620. $return = '';
  621. }
  622. if (method_exists($function, 'getAttributes') && ($attributes = $function->getAttributes())) {
  623. foreach ($attributes as $attribute) {
  624. $methodDocBlock .= "\n #[".$attribute->getName().']';
  625. }
  626. }
  627. $methods .= "\n$methodDocBlock\n public$static function $method($parameters)$return;";
  628. }
  629. $files->$interface = strtr(preg_replace_callback('/(\/\/ <methods[\s\S]*>)([\s\S]+)(<\/methods>)/mU', function ($matches) use ($methods) {
  630. return $matches[1]."$methods\n\n // ".$matches[3];
  631. }, $files->$interface, 1), [
  632. 'CarbonInterface::TRANSLATE_ALL' => 'self::TRANSLATE_ALL',
  633. ]);
  634. $factories = [
  635. __DIR__.'/src/Carbon/Factory.php' => $staticMethods,
  636. __DIR__.'/src/Carbon/FactoryImmutable.php' => $staticImmutableMethods,
  637. ];
  638. foreach ($factories as $file => $methods) {
  639. $autoDoc = compileDoc($methods, $file);
  640. $content = file_get_contents($file);
  641. $files->$file = preg_replace_callback('/(<autodoc[\s\S]*>)([\s\S]+)(<\/autodoc>)/mU', function ($matches) use ($file, $autoDoc) {
  642. return $matches[1]."\n *$autoDoc\n *\n * ".$matches[3];
  643. }, $content, 1);
  644. }
  645. foreach ($files as $file => $contents) {
  646. file_put_contents($file, $contents);
  647. }