nur-sery/nur_src/tools/compctl/ComposerWalker.php

116 lines
3.5 KiB
PHP

<?php
namespace nur\tools\compctl;
use Exception;
use nur\A;
class ComposerWalker {
protected static function get_project_dir($name, string $projects_dir) {
$project_dir = A::get(ConfigFile::PROJNAMES, $name);
if ($project_dir === null) {
$project_dir = preg_replace('/\//', "-", $name, 1);
}
return "$projects_dir/$project_dir";
}
static function parse_project(string $dir, array &$projects, ?array &$new_projects=null, bool $throw=false): ?array {
try {
$composer = new ComposerFile("$dir/composer.json");
} catch (Exception $e) {
if ($throw) throw $e;
else return null;
}
$pname = $composer->getv("name");
if (array_key_exists($pname, $projects) && $projects[$pname] !== null) {
# projet déjà analysé
return $projects[$pname];
}
$project = [
"composer" => $composer,
"name" => $pname,
"requires" => [],
"branches" => [],
"dir" => realpath($dir),
];
$requires = $composer->geta("require");
if ($requires !== null) {
foreach ($requires as $dep => $version) {
# ignorer les projets qui n'ont pas le préfixe requis
$include_project = false;
foreach (ConfigFile::PREFIXES as $prefix) {
if (substr($dep, 0, strlen($prefix)) === $prefix) {
$include_project = true;
break;
}
}
if (!$include_project) continue;
# sinon, ajouter le projet aux dépendances
$project["requires"][] = $dep;
if (!array_key_exists($dep, $projects)) {
$projects[$dep] = null;
$new_projects[$dep] = true;
}
if (substr($version, 0, 4) === "dev-") {
$project["branches"][$dep] = substr($version, 4);
} else {
$project["branches"][$dep] = $version;
}
}
}
$projects[$pname] = $project;
return $project;
}
static function build_projects(array $project_dirs, ?string &$projects_dir): array {
$projects = [];
foreach ($project_dirs as $project_dir) {
if ($projects_dir === null) $projects_dir = dirname($project_dir);
# analyser le projet initial
$new_projects = [];
if (self::parse_project($project_dir, $projects, $new_projects) === null) {
continue;
}
# puis analyser récursivement tous les projets dépendants
while (true) {
$have_new = false;
foreach ($new_projects as $name => &$new) {
if ($new) {
$new = false;
$have_new = true;
$project_dir = self::get_project_dir($name, $projects_dir);
self::parse_project($project_dir, $projects, $new_projects);
}
}; unset($new);
if (!$have_new) break;
}
}
return $projects;
}
static function order_deps(?array $deps, array $projects): array {
if ($deps === null) $deps = array_keys($projects);
$pi = 0;
$max = count($deps);
while ($pi < $max) {
$modified = false;
$project = $projects[$deps[$pi]];
if ($project !== null) {
# dépendance dont on a trouvé la correspondance sur disque
foreach ($project["requires"] as $dep) {
$di = array_search($dep, $deps);
if ($di > $pi) {
# si la dépendance se situe après le projet, la placer avant
$deps = array_splice($deps, $di, 1);
$deps = array_splice($deps, $pi, 0, [$dep]);
$modified = true;
}
}
}
if (!$modified) $pi++;
}
return $deps;
}
}