|
| 1 | +<?php |
| 2 | + |
| 3 | +declare(strict_types=1); |
| 4 | + |
| 5 | +namespace Araga; |
| 6 | + |
| 7 | +/** |
| 8 | + * Class AuxDataPath |
| 9 | + * |
| 10 | + * Helpers to determine sensible default paths when AuxData is installed |
| 11 | + * via Composer under the vendor/ directory. |
| 12 | + * |
| 13 | + * Typical Composer layout: |
| 14 | + * |
| 15 | + * project-root/ |
| 16 | + * composer.json |
| 17 | + * vendor/ |
| 18 | + * araga/ |
| 19 | + * aux-data/ |
| 20 | + * src/ |
| 21 | + * AuxData.php |
| 22 | + * AuxDataPath.php |
| 23 | + * |
| 24 | + * This class tries to: |
| 25 | + * - Locate the project root by walking up until it finds composer.json |
| 26 | + * - Provide a convenient storage directory under that root |
| 27 | + */ |
| 28 | +final class AuxDataPath |
| 29 | +{ |
| 30 | + /** |
| 31 | + * Maximum directory levels to traverse upwards when looking |
| 32 | + * for composer.json. |
| 33 | + */ |
| 34 | + private const MAX_ASCEND_LEVELS = 8; |
| 35 | + |
| 36 | + /** |
| 37 | + * Try to detect the project root directory. |
| 38 | + * |
| 39 | + * Strategy: |
| 40 | + * - Start from this file's directory (__DIR__) |
| 41 | + * - Walk up until we find "composer.json" |
| 42 | + * - Stop after MAX_ASCEND_LEVELS to avoid infinite loops |
| 43 | + * - If nothing is found, fall back to assuming a standard |
| 44 | + * vendor/araga/aux-data/src layout and go 4 levels up. |
| 45 | + * |
| 46 | + * @return string Absolute path to the detected project root. |
| 47 | + */ |
| 48 | + public static function projectRoot(): string |
| 49 | + { |
| 50 | + $dir = __DIR__; |
| 51 | + $previous = null; |
| 52 | + $levels = 0; |
| 53 | + |
| 54 | + while ($dir !== $previous && $levels < self::MAX_ASCEND_LEVELS) { |
| 55 | + if (file_exists($dir . DIRECTORY_SEPARATOR . 'composer.json')) { |
| 56 | + return $dir; |
| 57 | + } |
| 58 | + |
| 59 | + $previous = $dir; |
| 60 | + $dir = \dirname($dir); |
| 61 | + $levels++; |
| 62 | + } |
| 63 | + |
| 64 | + // Fallback: assume "vendor/araga/aux-data/src" structure |
| 65 | + // __DIR__ => .../vendor/araga/aux-data/src |
| 66 | + // dirname(__DIR__, 4) => .../project-root |
| 67 | + return \dirname(__DIR__, 4); |
| 68 | + } |
| 69 | + |
| 70 | + /** |
| 71 | + * Return a storage directory under the detected project root. |
| 72 | + * |
| 73 | + * Example (default): |
| 74 | + * {projectRoot}/storage |
| 75 | + * |
| 76 | + * Example (custom): |
| 77 | + * AuxDataPath::storageDir('var/aux-data') |
| 78 | + * => {projectRoot}/var/aux-data |
| 79 | + * |
| 80 | + * This method does NOT create the directory, it only computes the path. |
| 81 | + * |
| 82 | + * @param string $relative Relative directory from the project root. |
| 83 | + * |
| 84 | + * @return string Absolute path to the storage directory. |
| 85 | + */ |
| 86 | + public static function storageDir(string $relative = 'storage'): string |
| 87 | + { |
| 88 | + return self::projectRoot() |
| 89 | + . DIRECTORY_SEPARATOR |
| 90 | + . str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $relative); |
| 91 | + } |
| 92 | +} |
0 commit comments