├── .gitignore ├── composer.json ├── readme.md └── src ├── Classes └── PackageChecker.php ├── Http ├── Controllers │ └── PackageCheckerController.php └── Services │ └── PackageCheckerService.php ├── Providers └── PackageCheckerServiceProvider.php ├── config └── package-checker.php ├── resources └── views │ └── list.blade.php └── routes └── web.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | vendor/ 3 | ``` -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "iinmass/laravel-package-checker", 3 | "description": "A powerful tool for managing package dependencies in Laravel projects. Get insights into package versions, installation status, and sizes.", 4 | "type": "library", 5 | "autoload": { 6 | "psr-4": { 7 | "Iinmass\\LaravelPackageChecker\\": "src/" 8 | } 9 | }, 10 | "keywords": [ 11 | "laravel", 12 | "package", 13 | "checker", 14 | "dependency", 15 | "management" 16 | ], 17 | "license": "MIT", 18 | "extra": { 19 | "laravel": { 20 | "providers": [ 21 | "Iinmass\\LaravelPackageChecker\\Providers\\PackageCheckerServiceProvider" 22 | ] 23 | } 24 | }, 25 | "version": "2.1.1", 26 | "authors": [ 27 | { 28 | "name": "iinmass", 29 | "email": "inmass.idbel@gmail.com", 30 | "role": "Developer" 31 | } 32 | ], 33 | "minimum-stability": "stable", 34 | "require": { 35 | "php": "^8.0", 36 | "illuminate/support": "^9.0|^10.0", 37 | "laravel/framework": "9.*|10.*", 38 | "guzzlehttp/guzzle": "^7.0" 39 | }, 40 | "require-dev": { 41 | "phpunit/phpunit": "^9.0", 42 | "vimeo/psalm": "^4.30" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | [](https://github.com/inmass/laravel-package-checker/blob/main/LICENSE) 2 | [](https://packagist.org/packages/iinmass/laravel-package-checker) 3 | 4 | 5 | ## Features 6 | 7 | Retrieves a list of packages required in your project's composer.json file. 8 | Displays the current version, latest version, installation status, and size for each package. 9 | Offers a treemap visualization of the vendor directory, showing the size distribution of packages. 10 | Helps optimize project dependencies by highlighting large packages. 11 | Easy installation and seamless integration with your existing project. 12 | 13 | ## Description 14 | 15 | Package Checker is a powerful tool that provides insights into the packages used in your project. It helps you manage your project's dependencies, keeping you informed about the current and latest versions of each package, their installation status, and their sizes. The package also offers a treemap visualization that displays the size distribution of packages within your vendor directory, allowing you to identify large packages that might require optimization. 16 | 17 | ## Installation 18 | 19 | The Laravel Package Checker can be easily installed via Composer. Run the following command in your terminal: 20 | 21 | ```bash 22 | composer require iinmass/laravel-package-checker 23 | ``` 24 | 25 | After the installation is complete, the package will be ready to use in your Laravel project. 26 | 27 | ## Usage 28 | 29 | To access the Package Checker, navigate to the following URL in your web browser: `http://your-app-url/package-checker/list`. 30 | This page provides a comprehensive overview of your Laravel project's packages, including the required packages from composer.json, their versions, installation status, and sizes as well as the tree of all the packages and their dependencies. 31 | 32 | ## Configuration 33 | 34 | The Laravel Package Checker requires no additional configuration. It seamlessly integrates with your Laravel project and retrieves the necessary information from the composer.json file and vendor directory. 35 | 36 | Alternatively, you can publish the package's configuration file and customize it to suit your needs by running the following command in your terminal: 37 | ```bash 38 | php artisan vendor:publish --tag=package-checker-config 39 | ``` 40 | 41 | ## Contributing 42 | 43 | Contributions to the Laravel Package Checker are welcome! If you would like to contribute to the project, please follow these steps: 44 | 45 | 1. Fork the repository. 46 | 2. Create a new branch for your feature or fix. 47 | 3. Make the necessary modifications. 48 | 4. Commit your changes. 49 | 5. Push the branch to your fork. 50 | 6. Submit a pull request. 51 | Please ensure that your code adheres to the project's coding standards and includes appropriate tests. 52 | 53 | ## License 54 | 55 | The Laravel Package Checker is open-source software licensed under the [MIT license](https://opensource.org/licenses/MIT). 56 | 57 | ## Credits 58 | 59 | The Laravel Package Checker is developed and maintained by [inmass](https://github.com/inmass) 60 | 61 | ## Support 62 | 63 | If you encounter any issues or have any questions, please create a new issue on the [issue tracker](https://github.com/inmass/laravel-package-checker/issues). I will be happy to assist you. -------------------------------------------------------------------------------- /src/Classes/PackageChecker.php: -------------------------------------------------------------------------------- 1 | json($installedPackages); 19 | } 20 | 21 | public function getPackageDetails() 22 | { 23 | $data = [ 24 | 'name' => request('name'), 25 | 'version' => request('version'), 26 | ]; 27 | 28 | $requirements = PackageChecker::getPackageDetailsFor($data); 29 | return response()->json($requirements); 30 | } 31 | 32 | public function getLatestVersion() 33 | { 34 | $name = request('name'); 35 | $latestVersion = PackageChecker::getLatestVersion($name); 36 | return response()->json($latestVersion); 37 | } 38 | 39 | public function getPackageSize() 40 | { 41 | $name = request('name'); 42 | $requirements = request('requirements') ?? []; 43 | if (!$name) { 44 | return response()->json(['error' => 'Invalid request']); 45 | } 46 | $packageSize = PackageChecker::getPackageSize($name, $requirements); 47 | return response()->json($packageSize); 48 | } 49 | 50 | public function getVendorSize() 51 | { 52 | $vendorSize = PackageChecker::getAllPackageSizes(); 53 | $headOfResponse = []; 54 | $index = 0; 55 | foreach ($vendorSize as $package) { 56 | $headOfResponse[] = [ 57 | 'id' => 'ID_' . $index, 58 | 'name' => '', 59 | 'color' => '#' . substr(md5(rand()), 0, 6) 60 | ]; 61 | $index++; 62 | } 63 | $vendorSize = collect($vendorSize)->map(function ($item, $key) { 64 | return [ 65 | 'name' => $item['name'], 66 | 'parent' => 'ID_' . $key, 67 | 'value' => (int)$item['size'], 68 | ]; 69 | }); 70 | $vendorSize = array_merge($headOfResponse, $vendorSize->toArray()); 71 | return response()->json($vendorSize); 72 | } 73 | } -------------------------------------------------------------------------------- /src/Http/Services/PackageCheckerService.php: -------------------------------------------------------------------------------- 1 | getAsync($uri); 23 | } 24 | 25 | // Wait for all the requests to complete 26 | $results = \GuzzleHttp\Promise\Utils::settle($promises)->wait(); 27 | 28 | $this->packagesInfo = $results; 29 | } 30 | 31 | /** 32 | * Get the installed packages 33 | * 34 | * @return array 35 | */ 36 | public function getInstalledPackages(): array 37 | { 38 | $packages = []; 39 | 40 | // read the composer.json 41 | $composerJson = file_get_contents(base_path('composer.json')); 42 | 43 | // convert JSON to an associative array 44 | $composerConfig = json_decode($composerJson, true); 45 | 46 | // get the installed packages 47 | $installedPackages = $composerConfig['require']; 48 | 49 | // check if found and remove the php version 50 | if (isset($installedPackages['php'])) { 51 | unset($installedPackages['php']); 52 | } 53 | // remove php extensions 54 | foreach ($installedPackages as $key => $value) { 55 | if (str_contains($key, 'ext-')) { 56 | unset($installedPackages[$key]); 57 | } 58 | } 59 | 60 | // remove the "^" from the version number 61 | foreach ($installedPackages as $key => $value) { 62 | $installedPackages[$key] = str_replace('^', '', str_replace('v', '', $value)); 63 | // append to the packages array 64 | $packages[] = [ 65 | 'name' => $key, 66 | 'version' => $installedPackages[$key], 67 | 'latest_version' => '', 68 | 'status' => '', 69 | 'release_date' => '', 70 | 'requirements' => '', 71 | 'size' => '' 72 | ]; 73 | } 74 | 75 | return $packages; 76 | } 77 | 78 | 79 | /** 80 | * Get package information from packagist.org 81 | * 82 | * @param string $packageName 83 | * @return string 84 | */ 85 | private function getPackageInfo(string $packageName): array|bool 86 | { 87 | $client = new \GuzzleHttp\Client(); 88 | 89 | try { 90 | $response = $client->get("https://packagist.org/packages/$packageName.json"); 91 | return json_decode($response->getBody()->getContents(), true) ?? false; 92 | } catch (\GuzzleHttp\Exception\ClientException $e) { 93 | // means the package is not found 94 | return false; 95 | } 96 | } 97 | 98 | /** 99 | * Get the latest version of the package 100 | * 101 | * @param string $packageName 102 | * @return string 103 | */ 104 | public function getLatestVersion(array $packageInfo): string 105 | { 106 | if (!$packageInfo) { 107 | return 'Package not found'; 108 | } 109 | 110 | // If default_branch is set, it is generally the latest stable version 111 | if (isset($packageInfo['package']['default_branch'])) { 112 | $latestVersion = $packageInfo['package']['default_branch']; 113 | // Ensure this branch version exists in versions list and doesn't contain "-dev" 114 | if (isset($packageInfo['package']['versions'][$latestVersion]) && strpos($latestVersion, '-dev') === false) { 115 | return str_replace('v', '', $latestVersion); 116 | } 117 | } 118 | 119 | // Find the latest stable version manually, ignoring versions with "-dev", "-alpha", "-beta", or "-RC" 120 | foreach ($packageInfo['package']['versions'] as $version => $details) { 121 | if (!preg_match('/(dev|alpha|beta|RC)/', $version)) { 122 | // remove the "v" from the version number 123 | return str_replace('v', '', $version); 124 | } 125 | } 126 | 127 | return 'No stable version found'; 128 | } 129 | 130 | /** 131 | * Get the status of the packages 132 | * 133 | * @param array $installedPackages 134 | * @return array 135 | */ 136 | public function getPackageDetailsFor(array $package): array 137 | { 138 | $packageInfo = $this->getPackageInfo($package['name']); 139 | 140 | if (!$packageInfo) { 141 | return $this->createPackageNotFoundResponse(); 142 | } 143 | 144 | $desiredVersions = $this->filterDesiredVersions($packageInfo['package']['versions'], $package['version']); 145 | 146 | if (empty($desiredVersions)) { 147 | $latestVersion = $this->getLatestVersion($packageInfo); 148 | return $this->createUnknownVersionResponse($latestVersion); 149 | } 150 | 151 | $desiredVersion = end($desiredVersions); 152 | $versionReleaseDate = new \DateTime($desiredVersion['time']); 153 | $status = $this->determineVersionStatus($versionReleaseDate); 154 | 155 | $versionRequirements = $desiredVersion ? $this->extractVersionRequirements($desiredVersion['require']) : []; 156 | 157 | return [ 158 | 'status' => $status, 159 | 'release_date' => $versionReleaseDate ? $versionReleaseDate->format('Y-m-d') : '---', 160 | 'requirements' => $versionRequirements, 161 | 'latest_version' => $this->getLatestVersion($packageInfo) 162 | ]; 163 | } 164 | 165 | /** 166 | * Filter the desired versions based on the installed version 167 | * 168 | * @param array $versions 169 | * @param string $installedVersion 170 | * @return array 171 | */ 172 | private function filterDesiredVersions(array $versions, string $installedVersion): array 173 | { 174 | return array_filter($versions, function ($versionData) use ($installedVersion) { 175 | if (isset($versionData['version']) && is_string($versionData['version'])) { 176 | $version = ltrim($versionData['version'], 'v'); 177 | return preg_match("/^" . preg_quote($installedVersion, '/') . "/", $version); 178 | } 179 | return false; 180 | }); 181 | } 182 | 183 | /** 184 | * Determine the status of the version based on the release date 185 | * 186 | * @param \DateTime $releaseDate 187 | * @return string 188 | */ 189 | private function determineVersionStatus(\DateTime $releaseDate): string 190 | { 191 | $now = new \DateTime(); 192 | $diff = $now->diff($releaseDate); 193 | 194 | if ($diff->y >= 2) { 195 | return 'very_old'; 196 | } elseif ($diff->y >= 1) { 197 | return 'old'; 198 | } else { 199 | return 'good'; 200 | } 201 | } 202 | 203 | /** 204 | * Extract the version requirements, removing the PHP requirement if present 205 | * 206 | * @param array $requirements 207 | * @return array 208 | */ 209 | private function extractVersionRequirements(array $requirements): array 210 | { 211 | if (isset($requirements['php'])) { 212 | unset($requirements['php']); 213 | } 214 | return array_keys($requirements); 215 | } 216 | 217 | /** 218 | * Create a response for the package not found scenario 219 | * 220 | * @return array 221 | */ 222 | private function createPackageNotFoundResponse(): array 223 | { 224 | return [ 225 | 'status' => 'Package not found', 226 | 'release_date' => '---', 227 | 'requirements' => [] 228 | ]; 229 | } 230 | 231 | /** 232 | * Create a response for the unknown version scenario 233 | * 234 | * @return array 235 | */ 236 | private function createUnknownVersionResponse(string $latestVersion): array 237 | { 238 | return [ 239 | 'status' => 'Unknown version', 240 | 'release_date' => '---', 241 | 'requirements' => [], 242 | 'latest_version' => $latestVersion 243 | ]; 244 | } 245 | 246 | 247 | /** 248 | * Get the size of the package 249 | * 250 | * @param string $packageName 251 | * @return string 252 | */ 253 | public function getPackageSize(string $packageName, array $requirements): string 254 | { 255 | $packagePath = base_path("vendor/$packageName"); 256 | 257 | if (!file_exists($packagePath)) { 258 | return '---'; 259 | } 260 | 261 | $size = $this->getDirectorySize($packagePath); 262 | 263 | // if package has requirements 264 | if (!empty($requirements)) { 265 | foreach ($requirements as $requirement) { 266 | $requirementPath = base_path("vendor/$requirement"); 267 | if (file_exists($requirementPath)) { 268 | $size += $this->getDirectorySize($requirementPath) ?? 0; 269 | } 270 | } 271 | } 272 | 273 | return self::formatBytes($size); 274 | } 275 | 276 | /** 277 | * Get the size of the directory 278 | * 279 | * @param string $directory 280 | * @return string 281 | */ 282 | private function getDirectorySize(string $directory): string 283 | { 284 | $size = 0; 285 | foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($directory)) as $file) { 286 | if ($file->isFile()) { 287 | $size += $file->getSize(); 288 | } 289 | } 290 | 291 | return $size; 292 | } 293 | 294 | /** 295 | * Format bytes to human readable format 296 | * 297 | * @param int $bytes 298 | * @param int $precision 299 | * @return string 300 | */ 301 | public static function formatBytes(int $bytes): string 302 | { 303 | $units = array('B', 'KB', 'MB', 'GB', 'TB'); 304 | $i = 0; 305 | while ($bytes >= 1024 && $i < count($units) - 1) { 306 | $bytes /= 1024; 307 | $i++; 308 | } 309 | return round($bytes, 2) . ' ' . $units[$i]; 310 | } 311 | 312 | /** 313 | * Get the names and sizes of all packages in the vendor directory. 314 | * 315 | * @return array 316 | */ 317 | public function getAllPackageSizes(): array 318 | { 319 | $packages = $this->getAllPackageNames(); 320 | 321 | foreach ($packages as $key => $value) { 322 | $size = $this->getDirectorySize($value['path']); 323 | $packages[$key]['size'] = $size; 324 | } 325 | return $packages; 326 | } 327 | 328 | /** 329 | * Get the names of all packages in the vendor directory. 330 | * 331 | * @return array 332 | */ 333 | public function getAllPackageNames(): array 334 | { 335 | $installedPackagesFile = base_path('vendor/composer/installed.php'); 336 | // get returned array from installed.php 337 | if (!file_exists($installedPackagesFile)) { 338 | return []; 339 | } 340 | $installedPackagesArray = (require $installedPackagesFile)['versions'] ?? []; 341 | 342 | foreach ($installedPackagesArray as $key => $value) { 343 | if (!isset($value['install_path'])) { 344 | continue; 345 | } 346 | if (strpos($value['install_path'], '/../../') !== false) { 347 | continue; 348 | } 349 | $packages[] = [ 350 | 'name' => $key, 351 | 'path' => $value['install_path'], 352 | ]; 353 | } 354 | 355 | return $packages; 356 | } 357 | 358 | } -------------------------------------------------------------------------------- /src/Providers/PackageCheckerServiceProvider.php: -------------------------------------------------------------------------------- 1 | bind('laravel-package-checker', function () { 19 | return new \Iinmass\LaravelPackageChecker\Http\Services\PackageCheckerService(); 20 | }); 21 | } 22 | 23 | 24 | public function boot() 25 | { 26 | if ($this->app->runningInConsole()) { 27 | $this->setPublishers(); 28 | } 29 | 30 | $this->loadConfig(); 31 | $this->loadRoutes(); 32 | $this->loadViews(); 33 | 34 | } 35 | 36 | public function setPublishers() 37 | { 38 | $this->publishes([ 39 | __DIR__ . '/../config/package-checker.php' => config_path('package-checker.php') 40 | ], 'package-checker-config'); 41 | } 42 | 43 | /** 44 | * Group the routes and set up configurations to load them. 45 | * 46 | * @return void 47 | */ 48 | protected function loadRoutes() 49 | { 50 | Route::group($this->routesConfigurations(), function () { 51 | $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); 52 | }); 53 | } 54 | 55 | /** 56 | * Routes configurations. 57 | * 58 | * @return array 59 | */ 60 | private function routesConfigurations() 61 | { 62 | return [ 63 | 'prefix' => config('package-checker.routes.prefix'), 64 | 'middleware' => config('package-checker.routes.middleware'), 65 | 'namespace' => config('package-checker.routes.namespace'), 66 | ]; 67 | } 68 | 69 | /** 70 | * Load views. 71 | * 72 | * @return void 73 | */ 74 | protected function loadViews() 75 | { 76 | $this->loadViewsFrom(__DIR__.'/../resources/views', 'package-checker'); 77 | } 78 | 79 | /** 80 | * Load config. 81 | * 82 | * @return array 83 | */ 84 | protected function loadConfig() 85 | { 86 | $this->mergeConfigFrom( 87 | __DIR__.'/../config/package-checker.php', 'package-checker' 88 | ); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/config/package-checker.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'prefix' => env('PACKAGE_CHECKER_ROUTES_PREFIX', 'package-checker'), 6 | 'middleware' => env('PACKAGE_CHECKER_ROUTES_MIDDLEWARE', ['web','auth']), 7 | 'namespace' => 'Iinmass\LaravelPackageChecker\Http\Controllers', 8 | ], 9 | ]; -------------------------------------------------------------------------------- /src/resources/views/list.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |You can also see every installed package on your vendor from the chart below.
17 | 18 |Package Statuses:
20 | Good 21 | Old 22 | Very Old 23 |Package Name | 29 |Package Requirements | 30 |Version | 31 |Latest Version | 32 |Status | 33 |Release Date | 34 |Size | 35 |
---|---|---|---|---|---|---|
40 | 41 | Loading... 42 | | 43 |