From 78399c4710c66c479b14445bee2e6c8a0efd69cd Mon Sep 17 00:00:00 2001 From: Zi Xing Date: Fri, 25 Nov 2022 06:50:47 +0000 Subject: [PATCH] commit c64c037b4265d8e5bd57667652aa2c4399b9721b Author: Netkas Date: Fri Nov 25 01:04:27 2022 -0500 Added PackageNotFoundException.php :100644 100644 8387d15 9ffda6d M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 88bd370 A src/ncc/Exceptions/PackageNotFoundException.php 2 files changed, 36 insertions(+), 1 deletion(-) commit 8da4f532a146d626db1ee388eeb763ae14523589 Author: Netkas Date: Fri Nov 25 01:03:50 2022 -0500 Added uninstallation method to the CLI :100644 100644 73d7ad6 319eac9 M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 109 insertions(+), 1 deletion(-) commit 99298b12a7a3de539a9047016d4170db11fe0e61 Author: Netkas Date: Fri Nov 25 01:03:27 2022 -0500 Updated \ncc\Managers > PackageManager > uninstallPackageVersion() to save the PackageLock once modifications are done :100644 100644 174f704 3719e2b M src/ncc/Managers/PackageManager.php 1 file changed, 3 insertions(+) commit 44d8a489ccb15abb7ec7645c3f438d828b4a1570 Author: Netkas Date: Fri Nov 25 00:47:27 2022 -0500 Minor correction in \ncc\Utilities > Console > outException() :100644 100644 8646fbb 30bd0ff M src/ncc/Utilities/Console.php 1 file changed, 1 insertion(+), 1 deletion(-) commit 5132089e76f8a41976acf59655a80593ec11a39f Author: Netkas Date: Fri Nov 25 00:36:11 2022 -0500 Added void return type to \ncc\Managers > PackageManager > uninstallPackage() :100644 100644 d586c92 174f704 M src/ncc/Managers/PackageManager.php 1 file changed, 1 insertion(+), 1 deletion(-) commit 2390c6395cc60a5221a0c3064283200ed47273da Author: Netkas Date: Fri Nov 25 00:35:42 2022 -0500 Added method uninstallPackage() to \ncc\Managers > PackageManager :100644 100644 d84281b d586c92 M src/ncc/Managers/PackageManager.php 1 file changed, 33 insertions(+) commit 263f50a6680f26c47c04c92941ac9cc82b2b43d3 Author: Netkas Date: Fri Nov 25 00:35:12 2022 -0500 Added method uninstallPackageVersion() to \ncc\Managers > PackageManager :100644 100644 ddf601a d84281b M src/ncc/Managers/PackageManager.php 1 file changed, 59 insertions(+) commit cf4b7844bb256dbfb367e93907813bf9328517e2 Author: Netkas Date: Fri Nov 25 00:12:38 2022 -0500 Updated \ncc\Utilities > Console > outExceptionDetails() to display Error Code :100644 100644 cb7c985 8646fbb M src/ncc/Utilities/Console.php 1 file changed, 1 insertion(+), 1 deletion(-) commit 2bbef1ccbe9001d84be7306b721e7f11834f7cf5 Author: Netkas Date: Thu Nov 24 23:24:59 2022 -0500 Corrected parameters :100644 100644 d605afb 73d7ad6 M src/ncc/CLI/PackageManagerMenu.php :100644 100644 1348644 ddf601a M src/ncc/Managers/PackageManager.php :100644 100644 9e2b72d 3e4d055 M src/ncc/Objects/PackageLock.php 3 files changed, 5 insertions(+), 5 deletions(-) commit 7a0ed8206fd0feffd26a73aad246f93ab1ac5250 Author: Netkas Date: Thu Nov 24 18:29:27 2022 -0500 Added pkg_struct_1.0.png :000000 100644 0000000 31b102a A assets/pkg_struct_1.0.png 1 file changed, 0 insertions(+), 0 deletions(-) commit 58d2b0fd0ebf7b0fe54506fc3eae5cf0c7d44d7a Author: Netkas Date: Thu Nov 24 18:20:39 2022 -0500 Added ExitHandler Object to execution_policy.md :100644 100644 66106c9 f5b851b M docs/project_configuration/execution_policy.md 1 file changed, 32 insertions(+), 1 deletion(-) commit 188c840ed3a881829125efa22cb02019273b2c80 Author: Netkas Date: Thu Nov 24 18:13:19 2022 -0500 Corrected permission :100644 100644 3ab3f48 26732b9 M src/installer/installer 1 file changed, 1 insertion(+), 1 deletion(-) commit c5620f3d5c68777f28987b710c5b550a1e83fbf0 Author: Netkas Date: Thu Nov 24 17:58:16 2022 -0500 Various improvements :100644 100644 73ff4fa 3ab3f48 M src/installer/installer :100644 100644 ba501c9 ade05ec M src/ncc/Classes/PhpExtension/Installer.php :100644 100644 da030cc 4c7d6cb M src/ncc/Managers/PackageLockManager.php :100644 100644 2a87036 1348644 M src/ncc/Managers/PackageManager.php :100644 100644 630a9e0 1838950 M src/ncc/Utilities/Functions.php :100644 100644 947d8b1 5e08303 M src/ncc/Utilities/PathFinder.php 6 files changed, 18 insertions(+), 66 deletions(-) commit 9458446e18dc12dfff814e9c918f71c9fb033435 Author: Netkas Date: Thu Nov 24 17:28:58 2022 -0500 Optimized installer :100644 100644 6f9ae9c 73ff4fa M src/installer/installer 1 file changed, 5 insertions(+), 23 deletions(-) commit b8c88bf4226e88760df221bb95070b8c34f3768d Author: Netkas Date: Thu Nov 24 17:26:37 2022 -0500 Added method initializeFiles() to \ncc\Utilities > Functions :100644 100644 9aa235b 630a9e0 M src/ncc/Utilities/Functions.php 1 file changed, 79 insertions(+) commit c456a398932ca6864142e659ccec4de309c56398 Author: Netkas Date: Thu Nov 24 17:23:28 2022 -0500 Added method constructLockFile() in \ncc\Managers > PackageLockManager :100644 100644 0969e8f da030cc M src/ncc/Managers/PackageLockManager.php 1 file changed, 21 insertions(+) commit e4f07b382802bbd15df33a9c27ab4518c831b8f1 Author: Netkas Date: Thu Nov 24 17:17:39 2022 -0500 Removed two unused methods :100644 100644 0324a49 947d8b1 M src/ncc/Utilities/PathFinder.php 1 file changed, 47 deletions(-) commit cdd91090173e175e519e215e84abd7a557db0200 Author: Netkas Date: Thu Nov 24 17:06:35 2022 -0500 Unset $e :100644 100644 f7606f5 d605afb M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 1 insertion(+) commit dfc4ddc89b5fab99cc225361dcbc9a67e1b53b0b Author: Netkas Date: Thu Nov 24 17:06:15 2022 -0500 Implemented 'y' arg to \ncc\CLI > PackageManagerMenu > installPackage() :100644 100644 84c946e f7606f5 M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 8 insertions(+), 2 deletions(-) commit 145fa07b8a6efac13824a9d00f2ac2ad2db3a245 Author: Netkas Date: Thu Nov 24 17:02:12 2022 -0500 Minor optimization in \ncc\CLI > PackageManagerMenu > getInstalledPackages() :100644 100644 b5addce 84c946e M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 24 insertions(+), 22 deletions(-) commit 3e52a30096653e3bf212b4e7e36ce6f94d245ee3 Author: Netkas Date: Thu Nov 24 17:01:06 2022 -0500 Implemented method getInstalledPackages() to \ncc\CLI > PackageManagerMenu :100644 100644 97babba b5addce M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 67 insertions(+), 1 deletion(-) commit cb8e4a67378fb073a34da1f3921dacfb66cd0a95 Author: Netkas Date: Thu Nov 24 16:48:28 2022 -0500 Implemented installPackage() in \ncc\CLI > PackageManagerMenu :100644 100644 87306f5 97babba M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 109 insertions(+), 3 deletions(-) commit 5bfd226ba347eea55b99848d5ef30e0d29fb9e27 Author: Netkas Date: Tue Nov 22 02:02:30 2022 -0500 Updated method \ncc\Utilities > Console > formatColor() to respect the '--no-color' parameter :100644 100644 af70c49 cb7c985 M src/ncc/Utilities/Console.php 1 file changed, 5 insertions(+) commit fdd3c921cc734a6d9a385e520de7011a583aea63 Author: Netkas Date: Tue Nov 22 01:43:56 2022 -0500 Added package install check to \ncc\Managers > PackageManagers > install() :100644 100644 cbf94ce 2a87036 M src/ncc/Managers/PackageManager.php 1 file changed, 7 insertions(+) commit 5180174e8a0a26405eb775ceccad75db68144530 Author: Netkas Date: Tue Nov 22 01:41:49 2022 -0500 Added Exception PackageAlreadyInstalledException.php :100644 100644 d285c46 8387d15 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 fbf5c49 A src/ncc/Exceptions/PackageAlreadyInstalledException.php 2 files changed, 36 insertions(+), 1 deletion(-) commit c9923612fe689e83fb9301418c4faae6b14f1151 Merge: 5de462a 2a49dbc Author: Netkas Date: Tue Nov 22 01:34:50 2022 -0500 Merge remote-tracking branch 'origin/pkgmngr' into pkgmngr commit 5de462a45b378d94e22caf23e21ca1986d9c693b Author: Netkas Date: Tue Nov 22 01:18:28 2022 -0500 Load if null in \ncc\Managers > PackageLockManager > getPackageLock() Updated @throws in PackageManager.php Added method getPackages() to \ncc\Managers > PackageManager Added method getPackages() to \ncc\Objects > PackageLock Added parameter 'install_path' to \ncc\Objects\PackageLock > PackageEntry > addVersion() Added property 'Location' to \ncc\Objects\PackageLock > versionEntry Added method \ncc\Objects > PackageLock > getPackage() Added method getPackage(), getPackageVersion(), getLatestVersion() to \ncc\Managers > PackageManager :100644 100644 93d924d 0969e8f M src/ncc/Managers/PackageLockManager.php :100644 100644 5acd0a6 cbf94ce M src/ncc/Managers/PackageManager.php :100644 100644 e2e84d9 9e2b72d M src/ncc/Objects/PackageLock.php :100644 100644 3447fca 43cf7a9 M src/ncc/Objects/PackageLock/PackageEntry.php :100644 100644 88de3f0 f512c4a M src/ncc/Objects/PackageLock/VersionEntry.php 5 files changed, 106 insertions(+), 8 deletions(-) commit 2a49dbc224ec1328d040762857aa716f8b605041 Author: Netkas Date: Tue Nov 22 01:32:51 2022 -0500 Load if null in \ncc\Managers > PackageLockManager > getPackageLock() :100644 100644 93d924d 0969e8f M src/ncc/Managers/PackageLockManager.php 1 file changed, 3 insertions(+) commit f576d2cd48fc70b4573cc7636f4f30b1225ac6bc Author: Netkas Date: Tue Nov 22 01:32:13 2022 -0500 Updated @throws in PackageManager.php :100644 100644 c81b493 cbf94ce M src/ncc/Managers/PackageManager.php 1 file changed, 6 insertions(+) commit 5c9ab0dd00a2985ed98b61684e438c6d4081a92b Author: Netkas Date: Tue Nov 22 01:27:26 2022 -0500 Added method getPackages() to \ncc\Managers > PackageManager :100644 100644 16447d6 c81b493 M src/ncc/Managers/PackageManager.php 1 file changed, 11 insertions(+), 1 deletion(-) commit 6ae9ed251c0db0bc26be23444d06aa76287a705e Author: Netkas Date: Tue Nov 22 01:26:22 2022 -0500 Added method getPackages() to \ncc\Objects > PackageLock :100644 100644 97adbeb 9e2b72d M src/ncc/Objects/PackageLock.php 1 file changed, 13 insertions(+) commit f23673fe3fa2a22fd0e2d3cd3da71aced2124940 Author: Netkas Date: Tue Nov 22 01:20:53 2022 -0500 Added parameter 'install_path' to \ncc\Objects\PackageLock > PackageEntry > addVersion() :100644 100644 3447fca 43cf7a9 M src/ncc/Objects/PackageLock/PackageEntry.php 1 file changed, 3 insertions(+), 1 deletion(-) commit 80304b778984c88f92825a3de52c632f944e2e61 Author: Netkas Date: Tue Nov 22 01:19:57 2022 -0500 Added property 'Location' to \ncc\Objects\PackageLock > versionEntry :100644 100644 88de3f0 f512c4a M src/ncc/Objects/PackageLock/VersionEntry.php 1 file changed, 9 insertions(+) commit 74078ab6f1ebeee2f1de0c5a683ac178974949ca Author: Netkas Date: Tue Nov 22 01:19:07 2022 -0500 Added method \ncc\Objects > PackageLock > getPackage() :100644 100644 e2e84d9 97adbeb M src/ncc/Objects/PackageLock.php 1 file changed, 19 insertions(+), 2 deletions(-) commit 20e1a7caa4bef6a8d77a58d7ffed782e1d984b65 Author: Netkas Date: Tue Nov 22 01:18:28 2022 -0500 Added method getPackage(), getPackageVersion(), getLatestVersion() to \ncc\Managers > PackageManager :100644 100644 5acd0a6 16447d6 M src/ncc/Managers/PackageManager.php 1 file changed, 42 insertions(+), 4 deletions(-) commit 048d9774930fa435118a075853713c3410c6adc9 Author: Netkas Date: Tue Nov 22 00:58:48 2022 -0500 Removed extension dir in \ncc\Managers > PackageManager > install() :100644 100644 95a20f5 5acd0a6 M src/ncc/Managers/PackageManager.php 1 file changed, 1 insertion(+), 1 deletion(-) commit b1f64293d4e4b413341775beef3d97f8502ae90b Author: Netkas Date: Tue Nov 22 00:57:11 2022 -0500 Updated \ncc\Managers > PackageManager > install() :100644 100644 f83bdef 95a20f5 M src/ncc/Managers/PackageManager.php 1 file changed, 3 insertions(+), 2 deletions(-) commit b32c0c431a0e71cdf5118d9d92af342ff7bcbc4e Author: Netkas Date: Tue Nov 22 00:56:18 2022 -0500 Corrected return type in \ncc\Utilities > RuntimeCache > set() :100644 100644 622885e 996db9c M src/ncc/Utilities/RuntimeCache.php 1 file changed, 2 insertions(+), 2 deletions(-) commit 0f0ec6fead748e3f91f3966dd372c3d012eb874a Author: Netkas Date: Tue Nov 22 00:55:47 2022 -0500 Added PackageLock cache :100644 100644 f8e35d1 93d924d M src/ncc/Managers/PackageLockManager.php 1 file changed, 10 insertions(+) commit 84a51870bd6df93302eeae988ab2d448ca42e38f Author: Netkas Date: Tue Nov 22 00:44:53 2022 -0500 Added scope check to \ncc\Managers > PackageManager > install() :100644 100644 18dd0e4 f83bdef M src/ncc/Managers/PackageManager.php 1 file changed, 3 insertions(+) commit 8fd9d023d88992479461f9c3155b5d91e5a0b8d0 Author: Netkas Date: Tue Nov 22 00:24:49 2022 -0500 Added debugging and verbose statements :100644 100644 28a5210 a2a269f M src/ncc/CLI/Main.php :100644 100644 871e9aa 8ac9bae M src/ncc/Classes/NccExtension/PackageCompiler.php :100644 100644 5a86ecf d6df577 M src/ncc/Classes/PhpExtension/Compiler.php :100644 100644 c5c8e76 ba501c9 M src/ncc/Classes/PhpExtension/Installer.php :100644 100644 9524f49 f8e35d1 M src/ncc/Managers/PackageLockManager.php :100644 100644 2d37ba0 18dd0e4 M src/ncc/Managers/PackageManager.php 6 files changed, 45 insertions(+), 5 deletions(-) commit aec9944b3698f3e2ad07c76f3599f07ffc209165 Author: Netkas Date: Mon Nov 21 22:31:39 2022 -0500 Added Debugging messages in fwrite() and fread() in \ncc\Utilities > IO :100644 100644 aefa858 7b9c5be M src/ncc/Utilities/IO.php 1 file changed, 2 insertions(+) commit 8e4afc6ff84044a1f9954d1e98bc22dfa6a3bf43 Author: Netkas Date: Mon Nov 21 22:31:07 2022 -0500 Improved method cbc() in \ncc\Utilities > Functions to cache hash results in RuntimeCache :100644 100644 bd23434 9aa235b M src/ncc/Utilities/Functions.php 1 file changed, 19 insertions(+), 1 deletion(-) commit ff1ed0a41b3385a4389907b6f7a68d19ad626e2d Author: Netkas Date: Mon Nov 21 22:30:04 2022 -0500 Added \ncc\Utilities > RuntimeCache :000000 100644 0000000 622885e A src/ncc/Utilities/RuntimeCache.php 1 file changed, 40 insertions(+) commit 8e4bad74eaa00e5d231310feea576e88adbb790a Author: Netkas Date: Mon Nov 21 22:04:57 2022 -0500 Improved various functoins in Console.php :100644 100644 0969216 af70c49 M src/ncc/Utilities/Console.php 1 file changed, 70 insertions(+), 4 deletions(-) commit 4da4278d55af50c4f3d147ebf43f638b0edf1c5a Author: Netkas Date: Mon Nov 21 17:43:28 2022 -0500 addded validate() method to \ncc\Objects\ProjectConfiguration > ExecutionPolicy :100644 100644 151909c b627a30 M src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php 1 file changed, 10 insertions(+), 1 deletion(-) commit 65f159604e47f3608ecd2384ae270cb51bf0d4ff Author: Netkas Date: Mon Nov 21 17:41:57 2022 -0500 Changed verbose log prefix :100644 100644 cceedef 0969216 M src/ncc/Utilities/Console.php 1 file changed, 1 insertion(+), 1 deletion(-) commit 1e9c8dad0a12b25b2f98569b61879edfc23e9b49 Author: Netkas Date: Mon Nov 21 17:41:33 2022 -0500 Still add default to property in \ncc\CLI > Main > start() :100644 100644 088905a 28a5210 M src/ncc/CLI/Main.php 1 file changed, 9 insertions(+), 7 deletions(-) commit 857141d93a7e4a75425eb097962ff4f09b453d0e Author: Netkas Date: Mon Nov 21 17:41:05 2022 -0500 Corrected switch statement in \ncc\Utilities > Resolver > checkLogLevel() :100644 100644 bc3636a 4d172a0 M src/ncc/Utilities/Resolver.php 1 file changed, 11 insertions(+), 11 deletions(-) commit 94985da3d603e5e79597dd945a4a251bfbd4c357 Author: Netkas Date: Mon Nov 21 17:40:30 2022 -0500 Added debugging stuff :100644 100644 0041096 871e9aa M src/ncc/Classes/NccExtension/PackageCompiler.php 1 file changed, 11 insertions(+) commit 03920cec86228d086bf29fd72109ec8e0ba23c44 Author: Netkas Date: Mon Nov 21 17:40:04 2022 -0500 Added some debugging stuff :100644 100644 ab40863 5a86ecf M src/ncc/Classes/PhpExtension/Compiler.php 1 file changed, 5 insertions(+), 2 deletions(-) commit 37a594751808d3beb24933f279a2a78c812ae872 Author: Netkas Date: Mon Nov 21 16:18:29 2022 -0500 Updated method inlineProgressBar() in \ncc\Utilities > Console to return if log levels are Debug, Verbose or Silent. :100644 100644 af9ac44 cceedef M src/ncc/Utilities/Console.php 1 file changed, 14 insertions(+) commit a2aba15483962a091c4e515751eb4c08efc4e67d Author: Netkas Date: Mon Nov 21 16:15:13 2022 -0500 Various bug fixes :100644 100644 c34f9e5 088905a M src/ncc/CLI/Main.php :100644 100644 aa63562 c5c8e76 M src/ncc/Classes/PhpExtension/Installer.php :100644 100644 d315024 9524f49 M src/ncc/Managers/PackageLockManager.php :100644 100644 84be772 2d37ba0 M src/ncc/Managers/PackageManager.php :100644 100644 8011059 4dad3a6 M src/ncc/Objects/Package.php :100644 100644 fdf1136 e2e84d9 M src/ncc/Objects/PackageLock.php :100644 100644 df4071b 82be14e M src/ncc/Objects/PackageLock/DependencyEntry.php :100644 100644 7b27d0d 3447fca M src/ncc/Objects/PackageLock/PackageEntry.php :100644 100644 5be0cd7 88de3f0 M src/ncc/Objects/PackageLock/VersionEntry.php :100644 100644 2d5e5e8 aefa858 M src/ncc/Utilities/IO.php 10 files changed, 100 insertions(+), 79 deletions(-) commit ab02c328b945d888cc375d823a02370fcf1ca9b0 Author: Netkas Date: Mon Nov 21 14:23:34 2022 -0500 Added test load_package_lock.php :000000 100644 0000000 258c388 A tests/package_lock/load_package_lock.php 1 file changed, 9 insertions(+) commit 3db549c5661c4195287d36a5fc04c722ecadcbf7 Author: Netkas Date: Sun Nov 20 23:13:57 2022 -0500 Removed length null check for files that are empty (0 bytes) :100644 100644 c886bb2 2d5e5e8 M src/ncc/Utilities/IO.php 1 file changed, 2 deletions(-) commit 96272441672a5ef32dabf57e1dda58506f182aa9 Author: Netkas Date: Sun Nov 20 18:56:09 2022 -0500 Added Null-check in \ncc\Utilities > Resolver > checkLogLevel() :100644 100644 4dd84f3 bc3636a M src/ncc/Utilities/Resolver.php 1 file changed, 8 insertions(+), 3 deletions(-) commit 75bdd974a080559ee57b454d11a7afe55bf7ad3f Author: Netkas Date: Sun Nov 20 18:55:21 2022 -0500 - Added LineBreak for outException() - Use 0777 for `perms` when dumping exception details so non-root users can delete the file :100644 100644 ad4251a af9ac44 M src/ncc/Utilities/Console.php 1 file changed, 2 insertions(+), 2 deletions(-) commit 68dc12898294f9387140185b724612b03847dc81 Author: Netkas Date: Sun Nov 20 18:32:35 2022 -0500 Added execution_policy.md to docs :000000 100644 0000000 66106c9 A docs/project_configuration/execution_policy.md 1 file changed, 120 insertions(+) commit 24e1243ca44a172976d7c4e79385e13a220325ff Author: Netkas Date: Sat Nov 19 00:18:10 2022 -0500 Added constant compiling implementation for ExecutionUnits :100644 100644 9784ca4 0041096 M src/ncc/Classes/NccExtension/PackageCompiler.php :100644 100644 0c71cdd ab40863 M src/ncc/Classes/PhpExtension/Compiler.php :100644 100644 7afb335 84be772 M src/ncc/Managers/PackageManager.php 3 files changed, 113 insertions(+), 22 deletions(-) commit 9a4db305699ad841bc343910840ea8692524da40 Author: Netkas Date: Fri Nov 18 05:31:41 2022 -0500 Deleted unused Functions.php :100644 000000 dceff6e 0000000 D src/ncc/CLI/Functions.php 1 file changed, 31 deletions(-) commit 5b2efbc828a977cb060c6c7f96153c24f1135c61 Author: Netkas Date: Fri Nov 18 05:27:53 2022 -0500 Added ConstantReferences to \ncc\Abstracts :000000 100644 0000000 43cf7ae A src/ncc/Abstracts/ConstantReferences.php 1 file changed, 14 insertions(+) commit 73a52a112b97500c6cf2f0dc304585f562400d2f Author: Netkas Date: Fri Nov 18 00:40:19 2022 -0500 Renamed ProjectConstants to AssemblyConstants :100644 100644 b3facbb 1f277c4 R096 src/ncc/Abstracts/SpecialConstants/ProjectConstants.php src/ncc/Abstracts/SpecialConstants/AssemblyConstants.php :100644 100644 b561239 5ab4597 M src/ncc/Classes/NccExtension/ConstantCompiler.php 2 files changed, 11 insertions(+), 11 deletions(-) commit 286cd53f7a09c0a21a2d903d20ade5dccc67b200 Author: Netkas Date: Fri Nov 18 00:37:57 2022 -0500 Updated method install() in \ncc\Managers > PackageManager to handle ExecutionUnits and execute units pre- or post-installation stages :100644 100644 ca6abd7 7afb335 M src/ncc/Managers/PackageManager.php 1 file changed, 50 insertions(+), 17 deletions(-) commit d77e2900c8d9c6a089073a27710c6162ad0c487e Author: Netkas Date: Fri Nov 18 00:36:13 2022 -0500 Added method getExecutionUnit() to \ncc\Objects > Package :100644 100644 2ac9535 8011059 M src/ncc/Objects/Package.php 1 file changed, 17 insertions(+) commit 7fa280f6772b18e3f9c7586bbf4c31e8cee84b82 Author: Netkas Date: Fri Nov 18 00:35:48 2022 -0500 Implemented \ncc\Objects\Package > Installer :100644 100644 cbeb8b0 c368aac M src/ncc/Objects/Package/Installer.php 1 file changed, 74 insertions(+), 3 deletions(-) commit 0c86fcffa1e0bca94b122544f5a426e657488bc5 Author: Netkas Date: Fri Nov 18 00:35:26 2022 -0500 Added method temporaryExecute() to \ncc\Managers > ExecutionPointerManager and some optimizations & corrections to the class. :100644 100644 d68afa2 bedd2b5 M src/ncc/Managers/ExecutionPointerManager.php 1 file changed, 54 insertions(+), 7 deletions(-) commit 163aa8c1bf4b7d1fc10c12ff4c5fec08cc457308 Author: Netkas Date: Mon Nov 14 22:31:05 2022 -0500 Optimized Resolver.php :100644 100644 0e62cf9 4dd84f3 M src/ncc/Utilities/Resolver.php 1 file changed, 19 insertions(+), 4 deletions(-) commit 2671e79d1bc60093c30fc3d6be7c29af8956eff1 Author: Netkas Date: Mon Nov 14 20:55:01 2022 -0500 Added Runner.php :000000 100644 0000000 2133137 A src/ncc/Classes/NccExtension/Runner.php 1 file changed, 47 insertions(+) commit edab8bfb484fb890ba771f0e3f68a6d71e4a23ba Author: Netkas Date: Mon Nov 14 20:54:39 2022 -0500 Improved RunnerInterface to be more minimal :100644 100644 d77c5e1 fe8dbb4 M src/ncc/Classes/PhpExtension/Runner.php :100644 100644 f52c6ad 4d118c4 M src/ncc/Interfaces/RunnerInterface.php 2 files changed, 38 insertions(+), 16 deletions(-) commit 0ed789acca6a7fe6b22cb3e9d0f653f2a925c1bc Author: Netkas Date: Mon Nov 14 20:53:54 2022 -0500 Updated Constructor in \ncc\Objects\ExecutionPointers > ExecutionPointer :100644 100644 19d6b25 9a5747e M src/ncc/Objects/ExecutionPointers/ExecutionPointer.php 1 file changed, 2 insertions(+), 2 deletions(-) commit e8c1b1da41c0d6cb4f9875be7eeab9b24f174eda Author: Netkas Date: Mon Nov 14 20:53:26 2022 -0500 Updated Constructor and added methods toArray() and fromArray() to \ncc\Objects > ExecutionPointers :100644 100644 3ac4e56 9181c03 M src/ncc/Objects/ExecutionPointers.php 1 file changed, 46 insertions(+), 8 deletions(-) commit 03829e43ef6567cd193af6125d7c04b685ecd2ed Author: Netkas Date: Mon Nov 14 20:52:49 2022 -0500 Added small file delete condition in \ncc\Managers > ExecutionPointerManager > addUnit() :100644 100644 fc31539 d68afa2 M src/ncc/Managers/ExecutionPointerManager.php 1 file changed, 4 insertions(+) commit 807bee0236b77d0f6e75e57df8a46a2376f0caa2 Author: Netkas Date: Mon Nov 14 20:47:32 2022 -0500 Added ExecutionPointerManager.php :000000 100644 0000000 fc31539 A src/ncc/Managers/ExecutionPointerManager.php 1 file changed, 373 insertions(+) commit 7db5c31b8686624349eb8e1bc30e6dde74bcc66d Author: Netkas Date: Mon Nov 14 14:56:29 2022 -0500 Added ExecutionUnitNotFoundException.php Added NoAvailableUnitsException.php Added RunnerExecutionException.php Added ExecutionPointer.php Added ExecutionPointers.php :100644 100644 305dbeb d285c46 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 228f512 A src/ncc/Exceptions/ExecutionUnitNotFoundException.php :000000 100644 0000000 56d61ac A src/ncc/Exceptions/NoAvailableUnitsException.php :000000 100644 0000000 e568dd1 A src/ncc/Exceptions/RunnerExecutionException.php :000000 100644 0000000 3ac4e56 A src/ncc/Objects/ExecutionPointers.php :000000 100644 0000000 19d6b25 A src/ncc/Objects/ExecutionPointers/ExecutionPointer.php 6 files changed, 297 insertions(+), 1 deletion(-) commit f1b451ca290f81e78a66e50a25001f733f11cfe1 Author: Netkas Date: Mon Nov 14 14:28:15 2022 -0500 Added method getRunnerPath() to \ncc\Utilities > PathFinder :100644 100644 373c0fa 0324a49 M src/ncc/Utilities/PathFinder.php 1 file changed, 13 insertions(+) commit 4c49846b3585ee0550758a79daef86b16c249ba4 Author: Netkas Date: Mon Nov 14 14:27:43 2022 -0500 Added method exceedsPathLength() to \ncc\Utilities > Validate :100644 100644 bbfe3a6 368f6ac M src/ncc/Utilities/Validate.php 1 file changed, 14 insertions(+) commit d4cea6b095e09e649fca5e239f8ef47d02b92c52 Author: Netkas Date: Sun Nov 13 19:38:28 2022 -0500 Added PackageLockManager.php :000000 100644 0000000 d315024 A src/ncc/Managers/PackageLockManager.php 1 file changed, 115 insertions(+) commit e77e0408a8ba1990e3bb49861112b4f0badadcfa Author: Netkas Date: Sun Nov 13 19:38:02 2022 -0500 Added PackageManager.php :000000 100644 0000000 ca6abd7 A src/ncc/Managers/PackageManager.php 1 file changed, 247 insertions(+) commit ec8f00c4c7a74c8798fb9d302559b15909733ccb Author: Netkas Date: Sun Nov 13 19:35:10 2022 -0500 Updated Method \ncc\Objects > PackageLock > addPackage() to add versions even when package entry exists. :100644 100644 10bc0de fdf1136 M src/ncc/Objects/PackageLock.php 1 file changed, 5 insertions(+) commit 1f43d8b3c9496cdab9b06a0ada184363cfac68e7 Author: Netkas Date: Sun Nov 13 19:27:18 2022 -0500 Added MainExecutionPolicy property set in \ncc\Classes\PhpExtension > Compiler > prepare() :100644 100644 dbf710c 0c71cdd M src/ncc/Classes/PhpExtension/Compiler.php 1 file changed, 1 insertion(+) commit 95844595ae13c8eaa4d8a56c756da0d1e52fd96e Author: Netkas Date: Sun Nov 13 18:53:12 2022 -0500 Added 'AllConfigurations' to \ncc\Abstracts\Options > BuildConfigurationValues :100644 100644 3f90126 9b9d555 M src/ncc/Abstracts/Options/BuildConfigurationValues.php 1 file changed, 2 insertions(+) commit 3fa456ed888670e89eeae6f9a2de8c77a0495e9c Author: Netkas Date: Sun Nov 13 18:52:20 2022 -0500 Cleaned up build method in NCC :100644 100644 32d9731 e88d85a M src/ncc/CLI/BuildMenu.php :100644 100644 d03964a 9784ca4 M src/ncc/Classes/NccExtension/PackageCompiler.php :100644 100644 a9d0e42 286910b M src/ncc/Managers/ProjectManager.php 3 files changed, 216 insertions(+), 147 deletions(-) commit 300d6326082e0b603a61e2a0eff0dfe0350cc768 Author: Netkas Date: Sun Nov 13 18:51:25 2022 -0500 Added logging properties in \ncc\CLI > Main :100644 100644 74151af c34f9e5 M src/ncc/CLI/Main.php 1 file changed, 33 insertions(+) commit 6edf0fb514ea4ef079fd013f58a2c0f046abbf36 Author: Netkas Date: Sun Nov 13 16:52:58 2022 -0500 Implemented LogLevel Added LogLevel.php :000000 100644 0000000 6817c6e A src/ncc/Abstracts/LogLevel.php :100644 100644 f91cf37 ad4251a M src/ncc/Utilities/Console.php :100644 100644 036248a 0e62cf9 M src/ncc/Utilities/Resolver.php :100644 100644 77362be bbfe3a6 M src/ncc/Utilities/Validate.php 4 files changed, 179 insertions(+), 1 deletion(-) commit 729d84168869591be79b463a60314b8511cfe8c4 Author: Netkas Date: Sun Nov 13 16:21:14 2022 -0500 Added ExceptionHandling in Console.php :100644 100644 f08f5fa f91cf37 M src/ncc/Utilities/Console.php 1 file changed, 6 insertions(+), 10 deletions(-) commit 3daad842b9278b5e89e04a7e00367bda108c0a04 Author: Netkas Date: Sun Nov 13 12:51:15 2022 -0500 Added ProjectConfigurationNotFoundException.php :100644 100644 e12d9f9 305dbeb M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 2897f72 A src/ncc/Exceptions/ProjectConfigurationNotFoundException.php 2 files changed, 21 insertions(+), 1 deletion(-) commit dc110111dcc57e6dae272340bc82da5c1b654823 Author: Netkas Date: Sat Nov 12 07:15:48 2022 -0500 Formatting :100644 100644 6abd7e5 87306f5 M src/ncc/CLI/PackageManagerMenu.php 1 file changed, 1 insertion(+), 2 deletions(-) commit 825023118cc951a1e6270d277a184c1474b926da Author: Netkas Date: Sat Nov 12 07:10:57 2022 -0500 Changed MainExecutionPolicy property type from ExecutionPolicy to string Added MainExecutionPolicy to \ncc\Objects\PackageLock > VersionEntry :100644 100644 5da50a1 2ac9535 M src/ncc/Objects/Package.php :100644 100644 f209b68 7b27d0d M src/ncc/Objects/PackageLock/PackageEntry.php :100644 100644 0387937 5be0cd7 M src/ncc/Objects/PackageLock/VersionEntry.php 3 files changed, 12 insertions(+), 5 deletions(-) commit f7a1b6ed1691b2edfe6ec145c50ad5f6dcc7534f Author: Netkas Date: Sat Nov 12 06:51:43 2022 -0500 Added methods removeVersion(), getLatestVersion(), getVersions() to \ncc\Objects\PackageLock > PackageEntry :100644 100644 13afbc6 f209b68 M src/ncc/Objects/PackageLock/PackageEntry.php 1 file changed, 125 insertions(+) commit 06c8792c568ab0e1e4fb41fb854dcd39d36adca9 Author: Netkas Date: Fri Nov 11 08:33:19 2022 -0500 Opt to use builtin base64_encode() and base64_decode() when available to improve performance. :100644 100644 f1d7b5e 9c75af3 M src/ncc/Utilities/Base64.php 1 file changed, 7 insertions(+) commit e16c3ebd746f6160fdc29f92b71664b85d9c40da Author: Netkas Date: Fri Nov 11 05:53:01 2022 -0500 Updated ID handler in \ncc\Objects\Package > PackageUnit :100644 100644 bbdfa52 75940cc M src/ncc/Objects/Package/ExecutionUnit.php 1 file changed, 13 insertions(+), 6 deletions(-) commit 04bc2eb99ed5c3ac1b047b5eb9b48d4af09c4a6d Author: Netkas Date: Fri Nov 11 04:53:36 2022 -0500 Updated Runner in \ncc\Classes\PhpExtension :100644 100644 202e501 d77c5e1 M src/ncc/Classes/PhpExtension/Runner.php :100644 100644 8be9927 f52c6ad M src/ncc/Interfaces/RunnerInterface.php 2 files changed, 6 insertions(+), 9 deletions(-) commit d78dd984a700f652d4c325b70eec1beb8f1d5c8b Author: Netkas Date: Fri Nov 11 04:29:13 2022 -0500 Added checks for the results in \ncc\Objects > ProjectConfiguration > getRequiredExecutionPolicies() :100644 100644 370181c f361a3e M src/ncc/Objects/ProjectConfiguration.php 1 file changed, 58 insertions(+) commit cd1d9407d6ce8ca46d16af52212870ef9685aa87 Author: Netkas Date: Fri Nov 11 04:12:37 2022 -0500 Added properties preBuild() and postBuild() to \ncc\Objects\ProjectConfiguration > Build :100644 100644 60101eb 71caa85 M src/ncc/Objects/ProjectConfiguration/Build.php 1 file changed, 18 insertions(+) commit 8cdddf60a42aba2edfce4e7cd881f695cc7eec56 Author: Netkas Date: Fri Nov 11 04:12:07 2022 -0500 Added Installer to \ncc\Objects\ProjectConfiguration :000000 100644 0000000 30dda88 A src/ncc/Objects/ProjectConfiguration/Installer.php 1 file changed, 88 insertions(+) commit eddb59ed47831fa71f8a10e5df0ee961b63d4ba0 Author: Netkas Date: Fri Nov 11 04:11:46 2022 -0500 Added PackageLock.php (\ncc\Objects > PackageLock) :000000 100644 0000000 10bc0de A src/ncc/Objects/PackageLock.php 1 file changed, 157 insertions(+) commit eae1ea7b25ca083e40869cf2777d9f51a5cf4a31 Author: Netkas Date: Fri Nov 11 04:10:20 2022 -0500 Implemented Execution Policy reference validation in \ncc\Objects > ProjectConfiguration added method getRequiredExecutionPolicies() to \ncc\Objects > ProjectConfiguration to return required execution policies for the selected build configuration Added method processBuildPolicies() to \ncc\Objects > ProjectConfiguration to return required execution policies for the given build configuration :100644 100644 c987271 370181c M src/ncc/Objects/ProjectConfiguration.php 1 file changed, 161 insertions(+), 2 deletions(-) commit 8d9a6e49f89fb078cb4e8aabd4e39e060deaa7c2 Author: Netkas Date: Fri Nov 11 04:03:19 2022 -0500 Added Public Constructor to \ncc\Objects\ProjectConfiguration\ExecutionPolicy > Execute :100644 100644 7e31ce8 0cc164a M src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php 1 file changed, 11 insertions(+) commit dbcd231358bf0f520921fd71d6a8db80f5b89429 Author: Netkas Date: Fri Nov 11 04:01:40 2022 -0500 Added PreBuild and PostBuild to \ncc\Objects\ProjectConfiguration > BuildConfiguration :100644 100644 cc80ef6 45a8bf5 M src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php 1 file changed, 23 insertions(+), 12 deletions(-) commit a204788ecfa0cec7bbc8802eefc2b08a1b976143 Author: Netkas Date: Fri Nov 11 03:59:51 2022 -0500 Added exception InvalidExecutionPolicyName.php :100644 100644 eb82f32 e12d9f9 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 2699960 A src/ncc/Exceptions/InvalidExecutionPolicyName.php 2 files changed, 22 insertions(+), 1 deletion(-) commit 3190426e4faf724006ab738d0cc3e3719f366258 Author: Netkas Date: Fri Nov 11 03:56:55 2022 -0500 Validate execution policy names - Added method executionPolicyName() to \ncc\Utilities > Validate - Added ExecutionPolicy to \ncc\Abstracts > RegexPatterns :100644 100644 7fef226 335af46 M src/ncc/Abstracts/RegexPatterns.php :100644 100644 3cb7600 77362be M src/ncc/Utilities/Validate.php 2 files changed, 23 insertions(+) commit 80dfc2796775fdf7d7ee307d38adea7552d28a36 Author: Netkas Date: Fri Nov 11 02:34:37 2022 -0500 Added UndefinedExecutionPolicyException.php to \ncc\Exception :100644 100644 b414bd4 eb82f32 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 8797b5e A src/ncc/Exceptions/UndefinedExecutionPolicyException.php 2 files changed, 36 insertions(+), 1 deletion(-) commit 1896c99840dfb758b1a0e1e12adc805667adb62c Author: Netkas Date: Fri Nov 11 01:56:39 2022 -0500 Added RuntimeConstants.php :000000 100644 0000000 751d28c A src/ncc/Abstracts/SpecialConstants/RuntimeConstants.php 1 file changed, 8 insertions(+) commit c83aff610ea3ecaa968e66f40e81612e52b7c466 Author: Netkas Date: Fri Nov 11 01:54:35 2022 -0500 Added property 'Tty' to \ncc\Objects\ProjectConfiguration > ExecutionPolicy :100644 100644 d8c3624 7e31ce8 M src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php 1 file changed, 9 insertions(+) commit bbd77ddf702572e8615b9d2c9edff70985f90596 Author: Netkas Date: Fri Nov 11 00:24:36 2022 -0500 Added Default Value to public constructor to \ncc\Objects\PackageLock > DependencyEntry :100644 100644 d84e6cd df4071b M src/ncc/Objects/PackageLock/DependencyEntry.php 1 file changed, 1 insertion(+), 1 deletion(-) commit dce75b9df4f7ad0bafe0664f4cf5f21f0750f557 Author: Netkas Date: Thu Nov 10 22:10:32 2022 -0500 Deleted Unused MainExecutionPolicy.php :100644 000000 c005578 0000000 D src/ncc/Objects/Package/MainExecutionPolicy.php 1 file changed, 30 deletions(-) commit d4145283801c9705ac5ca807f088ac4963752758 Author: Netkas Date: Thu Nov 10 22:10:13 2022 -0500 Corrected method fromArray() in \ncc\Objects\Package > Header to construct Compiler object :100644 100644 e090bc5 16a627e M src/ncc/Objects/Package/Header.php 1 file changed, 3 insertions(+) commit 1803e228cc060ef9aedbd7bca1060eb960ebeff2 Author: Netkas Date: Thu Nov 10 21:43:49 2022 -0500 Added VersionNotFoundException :100644 100644 e7db756 b414bd4 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 b7b4acc A src/ncc/Exceptions/VersionNotFoundException.php 2 files changed, 36 insertions(+), 1 deletion(-) commit 491c065342fbb02c2998552f77264660bcf3b7c7 Author: Netkas Date: Thu Nov 10 21:36:59 2022 -0500 Added method getVersion() to \ncc\Objects\PackageLock > PackageEntry and moved Compiler property to \ncc\Objects\PackageLock > VersionEntry :100644 100644 6247cce 13afbc6 M src/ncc/Objects/PackageLock/PackageEntry.php :100644 100644 b0e925a 0387937 M src/ncc/Objects/PackageLock/VersionEntry.php 2 files changed, 57 insertions(+), 30 deletions(-) commit 04b13b3a64e7223e765d4cd6808a0165996ea7cb Author: Netkas Date: Thu Nov 10 19:43:04 2022 -0500 Added Installer.php to \ncc\Classes\PhpExtension to handle the installation process of PHP packages :000000 100644 0000000 aa63562 A src/ncc/Classes/PhpExtension/Installer.php 1 file changed, 362 insertions(+) commit 19bf40fcf4a8e23a145eb71c6d30766b016d0348 Author: Netkas Date: Thu Nov 10 19:39:04 2022 -0500 Added method installUnit() to \ncc\Interfaces > RunnerInterface and updated \ncc\Classes\PhpExtension > Runner to reflect the change :100644 100644 459c4e9 202e501 M src/ncc/Classes/PhpExtension/Runner.php :100644 100644 7899998 8be9927 M src/ncc/Interfaces/RunnerInterface.php 2 files changed, 30 insertions(+) commit 8b0ddf39a04b6c0b4b191ca45e0f0a8a059d14a6 Author: Netkas Date: Thu Nov 10 19:21:10 2022 -0500 Minor Correction in src/ncc/CLI/BuildMenu.php Deleted ConstantCompiler.php Replaced file_get_contents() with IO::fread() in Component.php Replaced file_get_contents() with IO::fread() in Functions.php Code Cleanup in Functions.php (\ncc\Utilities > Functions) Improved CLI Exception handling, added parameter to dump exception to json file :100644 100644 98b69ac 32d9731 M src/ncc/CLI/BuildMenu.php :100644 100644 c8fe242 74151af M src/ncc/CLI/Main.php :100644 100644 68b7568 33292ad M src/ncc/Objects/NccVersionInformation/Component.php :100644 100644 870eeb9 f08f5fa M src/ncc/Utilities/Console.php :100644 000000 7f1e298 0000000 D src/ncc/Utilities/ConstantCompiler.php :100644 100644 b1ed843 bd23434 M src/ncc/Utilities/Functions.php 6 files changed, 118 insertions(+), 103 deletions(-) commit 754f09c96d70d88645d508f4956e982ca21ef1f0 Author: Netkas Date: Tue Nov 8 16:08:22 2022 -0500 Added method build() to the CompilerInterface and updated \ncc\Classes\PhpExtension > Compiler & \ncc\CLI > BuildMenu to reflect these changes :100644 100644 f7a4a84 98b69ac M src/ncc/CLI/BuildMenu.php :100644 100644 aaef4ca dbf710c M src/ncc/Classes/PhpExtension/Compiler.php :100644 100644 ee86c57 c098abf M src/ncc/Interfaces/CompilerInterface.php 3 files changed, 36 insertions(+), 5 deletions(-) commit b1af6841d0f0cec1cf4f23802a37017a6a1f4a9a Author: Netkas Date: Tue Nov 8 16:06:41 2022 -0500 Moved methods compileRuntimeConstants(), compilePackageConstants() and regularConstants() from \ncc\Classes\NccExtension > ConstantCompiler to \ncc\Classes\NccExtension > PackageCompiler :100644 100644 9f6f434 b561239 M src/ncc/Classes/NccExtension/ConstantCompiler.php :100644 100644 ca88d63 d03964a M src/ncc/Classes/NccExtension/PackageCompiler.php 2 files changed, 64 insertions(+), 57 deletions(-) commit 3dd754ae8eddf3bd2f8cd800551d08f5943878d9 Author: Netkas Date: Mon Nov 7 22:41:17 2022 -0500 Minor change in IO.php Added method compileRunner() to \ncc\Utilities > Functions Updated \ncc\Objects > Package Added Exec and ExitHandlers to array converters in \ncc\Objects\ProjectConfiguration > ExecutionPolicy Made \ncc\Objects\ProjectConfiguration > Build > getBuildConfiguration() accept default values Updated Checksum handler in \ncc\Objects\Package > Resource Added ExecutionUnit to \ncc\Objects\Package Changed Checksum handler in \ncc\Objects\Package > Component Updated BuildMenu.php Added DateTimeConstants.php Added PackageLockVersion to \ncc\Abstracts > Versions Added Abstract Class Runners to \ncc\Abstracts Added NccExtension Added Runner.php to PhpExtension Updated Compiler.php for PhpExtension Added RunnerInterface.php Added UnsupportedRunnerException.php :100644 100644 9e74821 e7db756 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 5a0e052 A src/ncc/Abstracts/Runners.php :000000 100644 0000000 479346d A src/ncc/Abstracts/SpecialConstants/DateTimeConstants.php :100644 100644 98825ac 8676dba M src/ncc/Abstracts/Versions.php :100644 100644 e936406 f7a4a84 M src/ncc/CLI/BuildMenu.php :000000 100644 0000000 9f6f434 A src/ncc/Classes/NccExtension/ConstantCompiler.php :000000 100644 0000000 ca88d63 A src/ncc/Classes/NccExtension/PackageCompiler.php :100644 100644 0ce15f9 aaef4ca M src/ncc/Classes/PhpExtension/Compiler.php :000000 100644 0000000 459c4e9 A src/ncc/Classes/PhpExtension/Runner.php :000000 100644 0000000 830a7a1 A src/ncc/Exceptions/UnsupportedRunnerException.php :100644 100644 409ea5e ee86c57 M src/ncc/Interfaces/CompilerInterface.php :000000 100644 0000000 7899998 A src/ncc/Interfaces/RunnerInterface.php :100644 100644 0189a0d 5da50a1 M src/ncc/Objects/Package.php :100644 100644 75406f7 b48708b M src/ncc/Objects/Package/Component.php :000000 100644 0000000 bbdfa52 A src/ncc/Objects/Package/ExecutionUnit.php :100644 100644 87c44a0 14d77dd M src/ncc/Objects/Package/Resource.php :100644 100644 b27e917 60101eb M src/ncc/Objects/ProjectConfiguration/Build.php :100644 100644 8a68586 151909c M src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php :100644 100644 8e0fcaf b1ed843 M src/ncc/Utilities/Functions.php :100644 100644 b05f7ee c886bb2 M src/ncc/Utilities/IO.php 20 files changed, 896 insertions(+), 191 deletions(-) commit f46289a8083a2c3f396c238de2bdcd8129bc255f Author: Netkas Date: Mon Nov 7 22:38:40 2022 -0500 Added Intellij scopes - NCC_Source_files.xml - Third_Party_Source_Files.xml :000000 100644 0000000 6e38ebb A .idea/scopes/NCC_Source_files.xml :000000 100644 0000000 73f6682 A .idea/scopes/Third_Party_Source_Files.xml 2 files changed, 6 insertions(+) commit 7f36877919283a036d98590bc439910647029426 Author: Netkas Date: Mon Nov 7 14:51:17 2022 -0500 Corrected compileInstallConstants() in \ncc\Utilities :100644 100644 2b00bb6 7f1e298 M src/ncc/Utilities/ConstantCompiler.php 1 file changed, 4 insertions(+), 4 deletions(-) commit 2b820db54dd63ff5ce650de5c8002a76d4815c76 Author: Netkas Date: Mon Nov 7 13:14:21 2022 -0500 Reworked Constant Compiler to be accessible for different stages :000000 100644 0000000 0d4e857 A src/ncc/Abstracts/SpecialConstants/BuildConstants.php :000000 100644 0000000 93ba75a A src/ncc/Abstracts/SpecialConstants/InstallConstants.php :100644 100644 7d826c5 b3facbb R064 src/ncc/Abstracts/SpecialFormat.php src/ncc/Abstracts/SpecialConstants/ProjectConstants.php :000000 100644 0000000 2b00bb6 A src/ncc/Utilities/ConstantCompiler.php :100644 100644 79d40aa 8e0fcaf M src/ncc/Utilities/Functions.php 5 files changed, 128 insertions(+), 40 deletions(-) commit bdf083d1fcc03b77887cb872508a45acdca8d1d1 Author: Netkas Date: Mon Nov 7 12:38:01 2022 -0500 Updated ProjectConfiguration.php :100644 100644 293101c c987271 M src/ncc/Objects/ProjectConfiguration.php 1 file changed, 1 insertion(+) commit 7cac73e748e14212b7de13183e7e1a68126ece0e Author: Netkas Date: Mon Nov 7 12:35:06 2022 -0500 Updated Build.php to contain new property `main` :100644 100644 cf45987 b27e917 M src/ncc/Objects/ProjectConfiguration/Build.php 1 file changed, 10 insertions(+) commit a81fbd3bf70f09124308583b9c6087614fb30a92 Merge: baac4e4 ca76f8c Author: Netkas Date: Mon Nov 7 12:13:37 2022 -0500 Merge remote-tracking branch 'origin/pkgmngr' into pkgmngr commit baac4e42612c78f638c053f34fb5ec6eb411e86f Author: Netkas Date: Sun Nov 6 17:40:47 2022 -0500 Added PackageEntry.php Added DependencyEntry.php Added VersionEntry.php Added MainExecutionPolicy Updated CompilerInterface.php Added InstallerInterface.php Added InstallationPaths.php Added MainExecutionPolicy and sub-objects to ProjectConfiguration.php :100644 100644 4fa160d 409ea5e M src/ncc/Interfaces/CompilerInterface.php :000000 100644 0000000 75049ce A src/ncc/Interfaces/InstallerInterface.php :000000 100644 0000000 d7c745a A src/ncc/Objects/InstallationPaths.php :100644 100644 2869f8a 0189a0d M src/ncc/Objects/Package.php :100644 100644 8b66ae3 d84e6cd R077 src/ncc/Objects/PackageLock/Dependency.php src/ncc/Objects/PackageLock/DependencyEntry.php :000000 100644 0000000 6247cce A src/ncc/Objects/PackageLock/PackageEntry.php :100644 100644 2d002d9 b0e925a R069 src/ncc/Objects/PackageLock/Package.php src/ncc/Objects/PackageLock/VersionEntry.php :100644 100644 f2980b1 293101c M src/ncc/Objects/ProjectConfiguration.php :000000 100644 0000000 8a68586 A src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php :000000 100644 0000000 d8c3624 A src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php :000000 100644 0000000 8d78bcc A src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php :000000 100644 0000000 28a73b2 A src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandlers.php 12 files changed, 601 insertions(+), 27 deletions(-) commit 1871f384e08fa5e110c309d0c6b770179775bd0b Author: Netkas Date: Sun Nov 6 14:10:49 2022 -0500 Deleted unused PackageParser.php :100644 000000 1d54da1 0000000 D src/ncc/Classes/PackageParser.php 1 file changed, 42 deletions(-) commit 77b257d57c2858651f91c42a1802abc4700f3269 Author: Netkas Date: Sun Nov 6 12:25:29 2022 -0500 IO Correction & Escaped shell arguments (https://git.n64.cc/nosial/ncc/-/issues/5) Check if parent object is directory Improved IO handling in CredentialManager.php Improved IO handling in installer Added method fread() to \ncc\Utilities > IO Added method fwrite() to \ncc\Utilities > IO Updated installer to have better IO handling Updated installer with improvements and bug fix for Composer's automatic installation method [Issue #22](https://git.n64.cc/nosial/ncc/-/issues/22) Added IOException.php Added IO.php :100644 100644 2121c4b 6f9ae9c M src/installer/installer :100644 100644 c68a1ea 9e74821 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 e733d0c A src/ncc/Exceptions/IOException.php :100644 100644 543e537 4a9b2c2 M src/ncc/Managers/CredentialManager.php :000000 100644 0000000 b05f7ee A src/ncc/Utilities/IO.php 5 files changed, 286 insertions(+), 76 deletions(-) commit ca76f8c9713b7825441ee5102b646e82baa532c9 Author: Netkas Date: Sun Nov 6 15:22:23 2022 -0500 IO Correction & Escaped shell arguments (https://git.n64.cc/nosial/ncc/-/issues/5) :100644 100644 32955ab 6f9ae9c M src/installer/installer 1 file changed, 5 insertions(+), 5 deletions(-) commit 56af17f6e51928d898a6a0eff5dd570cd4d64294 Author: Netkas Date: Sun Nov 6 15:21:06 2022 -0500 Check if parent object is directory :100644 100644 72c72e6 b05f7ee M src/ncc/Utilities/IO.php 1 file changed, 2 insertions(+), 2 deletions(-) commit 225596dd0720c8d5141fd2a9b7d21949bbeb1974 Author: Netkas Date: Sun Nov 6 15:20:20 2022 -0500 Improved IO handling in CredentialManager.php :100644 100644 543e537 4a9b2c2 M src/ncc/Managers/CredentialManager.php 1 file changed, 16 insertions(+), 16 deletions(-) commit 7e9d1cf98d5eca70882d5c290c3e78112c58f5d9 Author: Netkas Date: Sun Nov 6 14:44:13 2022 -0500 Improved IO handling in installer :100644 100644 ace496f 32955ab M src/installer/installer 1 file changed, 71 insertions(+), 29 deletions(-) commit 265d42d75de2313d7672b1485b3ecf45610aee63 Author: Netkas Date: Sun Nov 6 14:34:56 2022 -0500 Added method fread() to \ncc\Utilities > IO :100644 100644 8c38584 72c72e6 M src/ncc/Utilities/IO.php 1 file changed, 44 insertions(+), 1 deletion(-) commit 77bf4b77af22e56be75cd2d5c0c3239c5e14b6b7 Author: Netkas Date: Sun Nov 6 14:25:44 2022 -0500 Updated installer to have better IO handling :100644 100644 9d919ce ace496f M src/installer/installer 1 file changed, 31 insertions(+), 7 deletions(-) commit bc07921d199d5a7e83983c6a138049eefc9a5268 Author: Netkas Date: Sun Nov 6 14:10:49 2022 -0500 Deleted unused PackageParser.php :100644 000000 1d54da1 0000000 D src/ncc/Classes/PackageParser.php 1 file changed, 42 deletions(-) commit 60af4da72d379f02da72c9732c025151890bfa7b Author: Netkas Date: Sun Nov 6 12:31:14 2022 -0500 Updated installer with improvements and bug fix for Composer's automatic installation method [Issue #22](https://git.n64.cc/nosial/ncc/-/issues/22) :100644 100644 2121c4b 9d919ce M src/installer/installer 1 file changed, 40 insertions(+), 23 deletions(-) commit 1aa5cf8c52e3db381f3e70f14208a3079592a2b3 Author: Netkas Date: Sun Nov 6 12:25:54 2022 -0500 Added IOException.php :100644 100644 c68a1ea 9e74821 M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 e733d0c A src/ncc/Exceptions/IOException.php 2 files changed, 36 insertions(+), 1 deletion(-) commit 47b240bfcae5ad87ca368c9d5e14692bcf7e2a38 Author: Netkas Date: Sun Nov 6 12:25:29 2022 -0500 Added IO.php :000000 100644 0000000 8c38584 A src/ncc/Utilities/IO.php 1 file changed, 49 insertions(+) commit 70f88ccaf702aad3ff86a7cce4366237675686b5 Author: Netkas Date: Sun Nov 6 09:11:25 2022 -0500 Deleted unused AutoloaderGenerator.php :100644 000000 2f94c89 0000000 D src/ncc/Classes/PhpExtension/AutoloaderGenerator.php 1 file changed, 120 deletions(-) commit 88abe9d79abe308fe5cc198c7cab5171503038fa Author: Netkas Date: Sat Nov 5 07:16:04 2022 -0400 Updated ExceptionCodes.php Added ComponentChecksumException.php Added ComponentDecodeException.php Added InstallationException.php Added PackageLockException.php Added ResourceChecksumException.php Added UnsupportedComponentTypeException.php :100644 100644 0e0dfca c68a1ea M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 68bb520 A src/ncc/Exceptions/ComponentChecksumException.php :000000 100644 0000000 593255f A src/ncc/Exceptions/ComponentDecodeException.php :000000 100644 0000000 1b99c67 A src/ncc/Exceptions/InstallationException.php :000000 100644 0000000 b86c20c A src/ncc/Exceptions/PackageLockException.php :000000 100644 0000000 911a885 A src/ncc/Exceptions/ResourceChecksumException.php :000000 100644 0000000 ecd4c79 A src/ncc/Exceptions/UnsupportedComponentTypeException.php 7 files changed, 211 insertions(+), 1 deletion(-) commit aac8f8b769be51b47be1fa70e6bd03e62bf85ab1 Author: Netkas Date: Tue Nov 1 18:55:25 2022 -0400 Added method load() to \ncc\Objects > Package :100644 100644 78bf4dc 2869f8a M src/ncc/Objects/Package.php 1 file changed, 114 insertions(+), 3 deletions(-) commit 41e2b736394cc29f9a71ab7fca6642b1e8c297f9 Author: Netkas Date: Tue Nov 1 18:53:41 2022 -0400 Add default state for package type :100644 100644 df54f71 3e86446 M src/ncc/Objects/Package/MagicBytes.php 1 file changed, 6 insertions(+) commit 99ceabb7f9f78b68cebef124e10ff3eb0f3c5601 Author: Netkas Date: Tue Nov 1 18:53:14 2022 -0400 Removed CLI checks from Compiler.php :100644 100644 77fa4fc 0ce15f9 M src/ncc/Classes/PhpExtension/Compiler.php 1 file changed, 24 insertions(+), 43 deletions(-) commit 5d6200608366ec8b5af86f9345578662fd90bb27 Author: Netkas Date: Tue Nov 1 18:29:45 2022 -0400 Added comments to SpecialFormat.php :100644 100644 ab80064 7d826c5 M src/ncc/Abstracts/SpecialFormat.php 1 file changed, 40 insertions(+), 1 deletion(-) commit 38046d81019471e8ccdfaaf9f4e6b43f67d6928b Author: Netkas Date: Tue Nov 1 18:27:09 2022 -0400 Added the functionality to compile constants :100644 100644 4257002 77fa4fc M src/ncc/Classes/PhpExtension/Compiler.php :100644 100644 9d85b37 79d40aa M src/ncc/Utilities/Functions.php 2 files changed, 37 insertions(+) commit 98ccc60f82b33bf11a49382ca12216f5f81cc11c Author: Netkas Date: Tue Nov 1 17:43:30 2022 -0400 Removed unused byteEncode() and byteDecode() functions from \ncc\Utilities > Functions :100644 100644 8be1667 9d85b37 M src/ncc/Utilities/Functions.php 1 file changed, 24 deletions(-) commit 4b2d320f48ac2426fcb159fc8ab50525a32a7569 Author: Netkas Date: Tue Nov 1 17:31:35 2022 -0400 Added Dependency.php and Package.php :000000 100644 0000000 8b66ae3 A src/ncc/Objects/PackageLock/Dependency.php :000000 100644 0000000 2d002d9 A src/ncc/Objects/PackageLock/Package.php 2 files changed, 149 insertions(+) commit 6cfa358b887115a9eeefc41011ede29b1efd8e12 Author: Netkas Date: Sun Oct 30 17:48:49 2022 -0400 Updated \ncc\Utilities > Functions > cbc() to return a binary string representation instead of an integer :100644 100644 e0b74d9 8be1667 M src/ncc/Utilities/Functions.php 1 file changed, 3 insertions(+), 3 deletions(-) commit ef1871989934cf05be823f05a954751d7919fa19 Author: Netkas Date: Sun Oct 30 13:25:35 2022 -0400 Added PackageManagerMenu.php :000000 100644 0000000 6abd7e5 A src/ncc/CLI/PackageManagerMenu.php 1 file changed, 57 insertions(+) commit 843289e239c9e4b19e1d623b5b0881ec5f5da76b Author: Netkas Date: Sat Oct 29 09:27:27 2022 -0400 Added PackageStructureVersions.php :000000 100644 0000000 fd11063 A src/ncc/Abstracts/PackageStructureVersions.php 1 file changed, 12 insertions(+) commit 4b74ffe8f74fe81f72e67175d5f3bd53dec93b01 Author: Netkas Date: Fri Oct 28 03:45:48 2022 -0400 Added PackageParsingException.php :100644 100644 9ae32a2 0e0dfca M src/ncc/Abstracts/ExceptionCodes.php :000000 100644 0000000 70c1be7 A src/ncc/Exceptions/PackageParsingException.php 2 files changed, 36 insertions(+), 1 deletion(-) --- .idea/scopes/NCC_Source_files.xml | 3 + .idea/scopes/Third_Party_Source_Files.xml | 3 + assets/pkg_struct_1.0.png | Bin 0 -> 49966 bytes .../project_configuration/execution_policy.md | 151 ++++++ src/installer/installer | 257 +++++---- src/ncc/Abstracts/ConstantReferences.php | 14 + src/ncc/Abstracts/ExceptionCodes.php | 127 ++++- src/ncc/Abstracts/LogLevel.php | 30 ++ .../Options/BuildConfigurationValues.php | 2 + .../Abstracts/PackageStructureVersions.php | 12 + src/ncc/Abstracts/RegexPatterns.php | 2 + src/ncc/Abstracts/Runners.php | 8 + .../SpecialConstants/AssemblyConstants.php | 51 ++ .../SpecialConstants/BuildConstants.php | 26 + .../SpecialConstants/DateTimeConstants.php | 162 ++++++ .../SpecialConstants/InstallConstants.php | 14 + .../SpecialConstants/RuntimeConstants.php | 8 + src/ncc/Abstracts/SpecialFormat.php | 32 -- src/ncc/Abstracts/Versions.php | 5 + src/ncc/CLI/BuildMenu.php | 104 +--- src/ncc/CLI/Functions.php | 31 -- src/ncc/CLI/Main.php | 85 ++- src/ncc/CLI/PackageManagerMenu.php | 345 ++++++++++++ .../Classes/NccExtension/ConstantCompiler.php | 129 +++++ .../Classes/NccExtension/PackageCompiler.php | 307 +++++++++++ src/ncc/Classes/NccExtension/Runner.php | 47 ++ src/ncc/Classes/PackageParser.php | 42 -- .../PhpExtension/AutoloaderGenerator.php | 120 ----- src/ncc/Classes/PhpExtension/Compiler.php | 392 +++++++------- src/ncc/Classes/PhpExtension/Installer.php | 342 ++++++++++++ src/ncc/Classes/PhpExtension/Runner.php | 67 +++ .../Exceptions/ComponentChecksumException.php | 28 + .../Exceptions/ComponentDecodeException.php | 28 + .../ExecutionUnitNotFoundException.php | 8 + src/ncc/Exceptions/IOException.php | 28 + src/ncc/Exceptions/InstallationException.php | 28 + .../Exceptions/InvalidExecutionPolicyName.php | 14 + .../Exceptions/NoAvailableUnitsException.php | 28 + .../PackageAlreadyInstalledException.php | 28 + src/ncc/Exceptions/PackageLockException.php | 28 + .../Exceptions/PackageNotFoundException.php | 28 + .../Exceptions/PackageParsingException.php | 28 + .../ProjectConfigurationNotFoundException.php | 13 + .../Exceptions/ResourceChecksumException.php | 29 ++ .../Exceptions/RunnerExecutionException.php | 28 + .../UndefinedExecutionPolicyException.php | 28 + .../UnsupportedComponentTypeException.php | 28 + .../Exceptions/UnsupportedRunnerException.php | 11 + .../Exceptions/VersionNotFoundException.php | 28 + src/ncc/Interfaces/CompilerInterface.php | 68 ++- src/ncc/Interfaces/InstallerInterface.php | 59 +++ src/ncc/Interfaces/RunnerInterface.php | 44 ++ src/ncc/Managers/CredentialManager.php | 32 +- src/ncc/Managers/ExecutionPointerManager.php | 424 +++++++++++++++ src/ncc/Managers/PackageLockManager.php | 152 ++++++ src/ncc/Managers/PackageManager.php | 490 ++++++++++++++++++ src/ncc/Managers/ProjectManager.php | 206 ++++++-- src/ncc/Objects/ExecutionPointers.php | 171 ++++++ .../ExecutionPointers/ExecutionPointer.php | 78 +++ src/ncc/Objects/InstallationPaths.php | 61 +++ .../NccVersionInformation/Component.php | 15 +- src/ncc/Objects/Package.php | 169 +++++- src/ncc/Objects/Package/Component.php | 29 +- src/ncc/Objects/Package/ExecutionUnit.php | 71 +++ src/ncc/Objects/Package/Header.php | 3 + src/ncc/Objects/Package/Installer.php | 77 ++- src/ncc/Objects/Package/MagicBytes.php | 6 + .../Objects/Package/MainExecutionPolicy.php | 30 -- src/ncc/Objects/Package/Resource.php | 19 +- src/ncc/Objects/PackageLock.php | 194 +++++++ .../Objects/PackageLock/DependencyEntry.php | 64 +++ src/ncc/Objects/PackageLock/PackageEntry.php | 235 +++++++++ src/ncc/Objects/PackageLock/VersionEntry.php | 126 +++++ src/ncc/Objects/ProjectConfiguration.php | 253 ++++++++- .../Objects/ProjectConfiguration/Build.php | 32 ++ .../BuildConfiguration.php | 35 +- .../ProjectConfiguration/ExecutionPolicy.php | 98 ++++ .../ExecutionPolicy/Execute.php | 103 ++++ .../ExecutionPolicy/ExitHandle.php | 76 +++ .../ExecutionPolicy/ExitHandlers.php | 71 +++ .../ProjectConfiguration/Installer.php | 88 ++++ src/ncc/Utilities/Base64.php | 7 + src/ncc/Utilities/Console.php | 176 ++++++- src/ncc/Utilities/Functions.php | 161 +++++- src/ncc/Utilities/IO.php | 97 ++++ src/ncc/Utilities/PathFinder.php | 53 +- src/ncc/Utilities/Resolver.php | 114 +++- src/ncc/Utilities/RuntimeCache.php | 40 ++ src/ncc/Utilities/Validate.php | 50 ++ tests/package_lock/load_package_lock.php | 9 + 90 files changed, 6796 insertions(+), 847 deletions(-) create mode 100644 .idea/scopes/NCC_Source_files.xml create mode 100644 .idea/scopes/Third_Party_Source_Files.xml create mode 100644 assets/pkg_struct_1.0.png create mode 100644 docs/project_configuration/execution_policy.md create mode 100644 src/ncc/Abstracts/ConstantReferences.php create mode 100644 src/ncc/Abstracts/LogLevel.php create mode 100644 src/ncc/Abstracts/PackageStructureVersions.php create mode 100644 src/ncc/Abstracts/Runners.php create mode 100644 src/ncc/Abstracts/SpecialConstants/AssemblyConstants.php create mode 100644 src/ncc/Abstracts/SpecialConstants/BuildConstants.php create mode 100644 src/ncc/Abstracts/SpecialConstants/DateTimeConstants.php create mode 100644 src/ncc/Abstracts/SpecialConstants/InstallConstants.php create mode 100644 src/ncc/Abstracts/SpecialConstants/RuntimeConstants.php delete mode 100644 src/ncc/Abstracts/SpecialFormat.php delete mode 100644 src/ncc/CLI/Functions.php create mode 100644 src/ncc/CLI/PackageManagerMenu.php create mode 100644 src/ncc/Classes/NccExtension/ConstantCompiler.php create mode 100644 src/ncc/Classes/NccExtension/PackageCompiler.php create mode 100644 src/ncc/Classes/NccExtension/Runner.php delete mode 100644 src/ncc/Classes/PackageParser.php delete mode 100644 src/ncc/Classes/PhpExtension/AutoloaderGenerator.php create mode 100644 src/ncc/Classes/PhpExtension/Installer.php create mode 100644 src/ncc/Classes/PhpExtension/Runner.php create mode 100644 src/ncc/Exceptions/ComponentChecksumException.php create mode 100644 src/ncc/Exceptions/ComponentDecodeException.php create mode 100644 src/ncc/Exceptions/ExecutionUnitNotFoundException.php create mode 100644 src/ncc/Exceptions/IOException.php create mode 100644 src/ncc/Exceptions/InstallationException.php create mode 100644 src/ncc/Exceptions/InvalidExecutionPolicyName.php create mode 100644 src/ncc/Exceptions/NoAvailableUnitsException.php create mode 100644 src/ncc/Exceptions/PackageAlreadyInstalledException.php create mode 100644 src/ncc/Exceptions/PackageLockException.php create mode 100644 src/ncc/Exceptions/PackageNotFoundException.php create mode 100644 src/ncc/Exceptions/PackageParsingException.php create mode 100644 src/ncc/Exceptions/ProjectConfigurationNotFoundException.php create mode 100644 src/ncc/Exceptions/ResourceChecksumException.php create mode 100644 src/ncc/Exceptions/RunnerExecutionException.php create mode 100644 src/ncc/Exceptions/UndefinedExecutionPolicyException.php create mode 100644 src/ncc/Exceptions/UnsupportedComponentTypeException.php create mode 100644 src/ncc/Exceptions/UnsupportedRunnerException.php create mode 100644 src/ncc/Exceptions/VersionNotFoundException.php create mode 100644 src/ncc/Interfaces/InstallerInterface.php create mode 100644 src/ncc/Interfaces/RunnerInterface.php create mode 100644 src/ncc/Managers/ExecutionPointerManager.php create mode 100644 src/ncc/Managers/PackageLockManager.php create mode 100644 src/ncc/Managers/PackageManager.php create mode 100644 src/ncc/Objects/ExecutionPointers.php create mode 100644 src/ncc/Objects/ExecutionPointers/ExecutionPointer.php create mode 100644 src/ncc/Objects/InstallationPaths.php create mode 100644 src/ncc/Objects/Package/ExecutionUnit.php delete mode 100644 src/ncc/Objects/Package/MainExecutionPolicy.php create mode 100644 src/ncc/Objects/PackageLock.php create mode 100644 src/ncc/Objects/PackageLock/DependencyEntry.php create mode 100644 src/ncc/Objects/PackageLock/PackageEntry.php create mode 100644 src/ncc/Objects/PackageLock/VersionEntry.php create mode 100644 src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php create mode 100644 src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php create mode 100644 src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php create mode 100644 src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandlers.php create mode 100644 src/ncc/Objects/ProjectConfiguration/Installer.php create mode 100644 src/ncc/Utilities/IO.php create mode 100644 src/ncc/Utilities/RuntimeCache.php create mode 100644 tests/package_lock/load_package_lock.php diff --git a/.idea/scopes/NCC_Source_files.xml b/.idea/scopes/NCC_Source_files.xml new file mode 100644 index 0000000..6e38ebb --- /dev/null +++ b/.idea/scopes/NCC_Source_files.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/.idea/scopes/Third_Party_Source_Files.xml b/.idea/scopes/Third_Party_Source_Files.xml new file mode 100644 index 0000000..73f6682 --- /dev/null +++ b/.idea/scopes/Third_Party_Source_Files.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/assets/pkg_struct_1.0.png b/assets/pkg_struct_1.0.png new file mode 100644 index 0000000000000000000000000000000000000000..31b102aa4b17d6a3f3056a1a2bf4f836a43b602f GIT binary patch literal 49966 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 z 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ö>Vl15@Dvo4M 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 literal 0 HcmV?d00001 diff --git a/docs/project_configuration/execution_policy.md b/docs/project_configuration/execution_policy.md new file mode 100644 index 0000000..f5b851b --- /dev/null +++ b/docs/project_configuration/execution_policy.md @@ -0,0 +1,151 @@ +# Execution Policies + +**Updated on Sunday, November 20, 2022** + +An execution policy is a policy defined in the Project +configuration file (`project.json`) that can be used +to execute a script or program in any stage of the package + +For instance, you can have a script that is executed before +the build process starts, or in different installation stages +when the user is installing your package you can have a unit +run before or after the installation/uninstallation process +starts.# + +Use cases such as this allows you to properly implement +and control your program's files & assets that are not +handled by NCC's compiler extensions. + +## Table of Contents + + +* [Execution Policies](#execution-policies) + * [Table of Contents](#table-of-contents) + * [JSON Example](#json-example) + * [ExecutionPolicy Object](#executionpolicy-object) + * [Object Properties](#object-properties) + * [JSON Example](#json-example) + * [ExecutionConfiguration Object](#executionconfiguration-object) + * [Object Properties](#object-properties) + * [JSON Example](#json-example) + * [ExitHandler Object](#exithandler-object) + * [Object Properties](#object-properties) + * [JSON Example](#json-example) + + + +## JSON Example + +```json +{ + "execution_policies": { + "main": { + "runner": "php", + "message": "Running main %ASSEMBLY.PACKAGE%", + "exec": { + "target": "scripts/main.php", + "working_directory": "%INSTALL_PATH.SRC%", + "silent": false + } + }, + "hello_world": { + "runner": "shell", + "message": "Running HTOP", + "options": { + "htop": null + }, + "exec": { + "tty": true + } + } + } +} +``` + +------------------------------------------------------------ + +## ExecutionPolicy Object + +Execution Policies for your project **must** have unique +names, because they way you tell NCC to execute these +policies is by referencing their name in the configuration. + +Invalid names/undefined policies will raise errors when +building the project + +### Object Properties + +| Property Name | Value Type | Example Value | Description | +|-----------------|---------------------------------|----------------------|--------------------------------------------------------------------------------------------| +| `runner` | string | bash | The name of a supported runner instance, see runners in this document | +| `message` | string, null | Starting foo_bar ... | *Optional* the message to display before running the execution policy | +| `exec` | ExecutionConfiguration | N/A | The configuration object that tells how the runner should execute the process | +| `exit_handlers` | ExitHandlersConfiguration, null | N/A | *Optional* Exit Handler Configurations that tells NCC how to handle exits from the process | + +### JSON Example + +```json +{ + "name": "foo_bar", + "runner": "bash", + "message": "Running foo_bar ...", + "exec": null, + "exit_handlers": null +} +``` + +------------------------------------------------------------ + +## ExecutionConfiguration Object + +### Object Properties + +| Property Name | Value Type | Example Value | Description | +|---------------------|-------------------|---------------------------------|------------------------------------------------------------------------| +| `target` | `string` | scripts/foo_bar.bash | The target file to execute | +| `working_directory` | `string`, `null` | %INSTALL_PATH.SRC% | *optional* The working directory to execute the process in | +| `options` | `array`, `null` | {"run": null, "log": "verbose"} | Commandline Parameters to pass on to the target or process | +| `silent` | `boolean`, `null` | False | Indicates if the target should run silently, by default this is false. | +| `tty` | `boolean`, `null` | False | Indicates if the target should run in TTY mode | +| `timeout` | `integer`, `null` | 60 | The amount of seconds to wait before the process is killed | + +### JSON Example + +```json +{ + "target": "scripts/foo_bar.bash", + "working_directory": "%INSTALL_PATH.SRC%", + "options": {"run": null, "log": "verbose"}, + "silent": false, + "tty": false, + "timeout": 10 +} +``` + + +------------------------------------------------------------ + +## ExitHandler Object + +An exit handler is executed once the specified exit code is +returned or the process exits with an error or normally, if +an exit handler is specified it will be executed. + +### Object Properties + +| Property Name | Value Type | Example Value | Description | +|---------------|--------------------|---------------|------------------------------------------------------------------------------| +| `message` | `string` | Hello World! | The message to display when the exit handler is triggered | +| `end_process` | `boolean`, `null` | False | *optional* Kills the process after this exit handler is triggered | +| `run` | `string`, `null` | `null` | *optional* A execution policy to execute once this exit handler is triggered | +| `exit_code` | `int`, `null` | 1 | The exit code that triggers this exit handler | +### JSON Example + +```json +{ + "message": "Hello World", + "end_process": false, + "run": null, + "exit_code": 1 +} +``` \ No newline at end of file diff --git a/src/installer/installer b/src/installer/installer index 2121c4b..26732b9 100644 --- a/src/installer/installer +++ b/src/installer/installer @@ -12,12 +12,8 @@ exists($path)) { Console::outError('Missing file \'' . $path . '\', installation failed.', true, 1); exit(1); @@ -202,7 +199,7 @@ // Preform the checksum validation if(!$NCC_BYPASS_CHECKSUM) { - if(!file_exists($NCC_CHECKSUM)) + if(!$NCC_FILESYSTEM->exists($NCC_CHECKSUM)) { Console::outWarning('The file \'checksum.bin\' was not found, the contents of the program cannot be verified to be safe'); } @@ -210,7 +207,16 @@ { Console::out('Running checksum'); - $checksum = ZiProto::decode(file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'checksum.bin')); + try + { + $checksum = ZiProto::decode(IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'checksum.bin')); + } + catch(Exception $e) + { + Console::outError($e->getMessage(), true, 1); + return; + } + $checksum_failed = false; foreach($checksum as $path => $hash) @@ -239,24 +245,33 @@ } } - // Check for required extensions - $curl_available = true; - foreach(Validate::requiredExtensions() as $ext => $installed) + // Check the installed extensions and report + Console::out('Checking installed extensions...'); + $extensions = Validate::requiredExtensions(); + foreach($extensions as $ext => $installed) { - if(!$installed) + if($installed) { - switch($ext) - { - case 'curl': - Console::outWarning('This installer requires the \'curl\' extension to install composer'); - $curl_available = false; - break; - - default: - Console::outWarning('The extension \'' . $ext . '\' is not installed, compatibility without it is not guaranteed'); - break; - } + Console::out("$ext ... " . Console::formatColor("installed", ConsoleColors::LightGreen)); } + else + { + Console::out("$ext ... " . Console::formatColor("missing", ConsoleColors::LightRed)); + } + } + + // Check for curl if the installer requires it + $curl_available = true; + if(!$extensions['curl']) + { + if(getParameter($NCC_ARGS, 'install-composer') !== null) + { + Console::outError('This installer requires the \'curl\' extension to install composer', true, 1); + return; + } + + $curl_available = false; + Console::outWarning('The extension \'curl\' is not installed, the installer will not be able to install composer'); } // Attempt to load version information @@ -283,14 +298,16 @@ try { - Console::out($full_name . ' Version: ' . $component->getVersion()); + Console::out(Console::formatColor($full_name, ConsoleColors::Green) . ' Version: ' . Console::formatColor($component->getVersion(), ConsoleColors::LightMagenta)); } - catch (ComponentVersionNotFoundException $e) + catch (Exception $e) { - Console::outWarning('Cannot determine component version of ' . $full_name); + Console::outWarning('Cannot determine component version of ' . Console::formatColor($full_name, ConsoleColors::Green)); } } + Console::out('Starting installation'); + // Determine the installation path $skip_prompt = false; $install_dir_arg = getParameter($NCC_ARGS, 'install-dir'); @@ -304,7 +321,7 @@ exit(1); } - if(file_exists($install_dir_arg . DIRECTORY_SEPARATOR . 'ncc')) + if($NCC_FILESYSTEM->exists($install_dir_arg . DIRECTORY_SEPARATOR . 'ncc')) { Console::out('NCC Seems to already be installed, the installer will repair/upgrade your current install'); $NCC_INSTALL_PATH = $install_dir_arg; @@ -323,9 +340,9 @@ { $user_input = null; $user_input = Console::getInput("Installation Path (Default: $NCC_INSTALL_PATH): "); - if(strlen($user_input) > 0 && file_exists($user_input) && Validate::unixFilepath($user_input)) + if(strlen($user_input) > 0 && $NCC_FILESYSTEM->exists($user_input) && Validate::unixFilepath($user_input)) { - if(file_exists($user_input . DIRECTORY_SEPARATOR . 'ncc')) + if($NCC_FILESYSTEM->exists($user_input . DIRECTORY_SEPARATOR . 'ncc')) { $NCC_INSTALL_PATH = $user_input; break; @@ -346,7 +363,6 @@ } } - // Determine the data path $skip_prompt = false; $data_dir_arg = getParameter($NCC_ARGS, 'data-dir'); @@ -360,7 +376,7 @@ exit(1); } - if(file_exists($data_dir_arg . DIRECTORY_SEPARATOR . 'package.lck')) + if($NCC_FILESYSTEM->exists($data_dir_arg . DIRECTORY_SEPARATOR . 'package.lck')) { $NCC_DATA_PATH = $data_dir_arg; $skip_prompt = true; @@ -379,9 +395,9 @@ { $user_input = null; $user_input = Console::getInput("Data Path (Default: $NCC_DATA_PATH): "); - if(strlen($user_input) > 0 && file_exists($user_input) && Validate::unixFilepath($user_input)) + if(strlen($user_input) > 0 && $NCC_FILESYSTEM->exists($user_input) && Validate::unixFilepath($user_input)) { - if(file_exists($user_input . DIRECTORY_SEPARATOR . 'package.lck')) + if($NCC_FILESYSTEM->exists($user_input . DIRECTORY_SEPARATOR . 'package.lck')) { $NCC_DATA_PATH = $user_input; break; @@ -409,14 +425,17 @@ { $update_composer = true; } - elseif(getParameter($NCC_ARGS, 'install-composer') !== null) - { - $update_composer = true; - } else { - Console::out("Note: This doesn't affect your current install of composer (if you have composer installed)"); - $update_composer = Console::getBooleanInput('Do you want to install composer for NCC? (Recommended)'); + if(!$NCC_AUTO_MODE) + { + Console::out("Note: This doesn't affect your current install of composer (if you have composer installed)"); + $update_composer = Console::getBooleanInput('Do you want to install composer for NCC? (Recommended)'); + } + else + { + $update_composer = false; + } } } else @@ -424,6 +443,7 @@ $update_composer = false; } + if(!$NCC_AUTO_MODE) { if(!Console::getBooleanInput('Do you want install NCC?')) @@ -435,14 +455,22 @@ // Backup the configuration file $config_backup = null; - if(file_exists($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml')) + if($NCC_FILESYSTEM->exists($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml')) { Console::out('ncc.yaml will be updated'); - $config_backup = file_get_contents($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml'); + try + { + $config_backup = IO::fread($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml'); + } + catch(Exception $e) + { + Console::outError($e->getMessage(), true, 1); + return; + } } // Prepare installation - if(file_exists($NCC_INSTALL_PATH)) + if($NCC_FILESYSTEM->exists($NCC_INSTALL_PATH)) { try { @@ -455,35 +483,20 @@ } } - // Create the required directories - $required_dirs = [ - $NCC_INSTALL_PATH, - $NCC_DATA_PATH, - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'packages', - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'cache', - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'repos', - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'cache' . DIRECTORY_SEPARATOR . 'downloads', - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'config', - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'data', - $NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'ext', - ]; + $NCC_FILESYSTEM->mkdir($NCC_INSTALL_PATH, 0755); - $NCC_FILESYSTEM->mkdir($required_dirs, 0755); - $NCC_FILESYSTEM->chmod([$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'config'], 0777); - $NCC_FILESYSTEM->chmod([$NCC_DATA_PATH . DIRECTORY_SEPARATOR . 'cache'], 0777); - - // Verify directories exist - foreach($required_dirs as $dir) + try { - if(!file_exists($dir)) - { - Console::outError("Cannot create directory '$dir', please verify if you have write permissions to the directory."); - exit(1); - } + Functions::initializeFiles(); + } + catch(Exception $e) + { + Console::outError('Cannot initialize NCC files, ' . $e->getMessage()); + exit(1); } // Install composer - if($curl_available && $update_composer) + if($update_composer) { Console::out('Installing composer for NCC'); @@ -501,11 +514,10 @@ fclose($fp); Console::out('Running composer installer'); - // TODO: Unescaped shell arguments are a security issue $Process = Process::fromShellCommandline(implode(' ', [ $NCC_PHP_EXECUTABLE, - $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer-setup.php', - '--install-dir=' . $NCC_INSTALL_PATH, + escapeshellcmd($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'composer-setup.php'), + '--install-dir=' . escapeshellcmd($NCC_INSTALL_PATH), '--filename=composer.phar' ])); $Process->setWorkingDirectory($NCC_INSTALL_PATH); @@ -536,7 +548,15 @@ // Install NCC Console::out('Copying files to \'' . $NCC_INSTALL_PATH . '\''); - $build_files = explode("\n", file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'build_files')); + try + { + $build_files = explode("\n", IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'build_files')); + } + catch(Exception $e) + { + Console::outError($e->getMessage(), true, 1); + return; + } $total_items = count($build_files); $processed_items = 0; @@ -561,7 +581,7 @@ $NCC_FILESYSTEM->copy(__DIR__ . DIRECTORY_SEPARATOR . $file, $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . $file); $NCC_FILESYSTEM->chmod([$NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . $file], 0755); - if(!file_exists($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . $file)) + if(!$NCC_FILESYSTEM->exists($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . $file)) { Console::outError('Cannot create file \'' . $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . $file . '\', installation failed.'); exit(1); @@ -573,32 +593,19 @@ Console::inlineProgressBar($processed_items, $total_items); } - // Create credential store if needed - Console::out('Processing Credential Store'); - $credential_manager = new CredentialManager(); - - try - { - $credential_manager->constructStore(); - } - catch (AccessDeniedException|\ncc\Exceptions\RuntimeException $e) - { - Console::outError('Cannot construct credential store, ' . $e->getMessage() . ' (Error Code: ' . $e->getCode() . ')'); - } - - try - { - $NCC_FILESYSTEM->touch([PathFinder::getPackageLock(Scopes::System)]); - } - catch (InvalidScopeException $e) - { - Console::outError('Cannot create package lock, ' . $e->getMessage()); - exit(0); - } - // Generate executable shortcut Console::out('Creating shortcut'); - $executable_shortcut = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'ncc.sh'); + + try + { + $executable_shortcut = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'ncc.sh'); + } + catch(Exception $e) + { + Console::outError($e->getMessage(), true, 1); + return; + } + $executable_shortcut = str_ireplace('%php_exec', $NCC_PHP_EXECUTABLE, $executable_shortcut); $executable_shortcut = str_ireplace('%ncc_exec', $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc', $executable_shortcut); @@ -611,41 +618,65 @@ foreach($bin_paths as $path) { // Delete old versions of the executable shortcuts. - if(file_exists($path . DIRECTORY_SEPARATOR . 'ncc')) + if($NCC_FILESYSTEM->exists($path . DIRECTORY_SEPARATOR . 'ncc')) { $NCC_FILESYSTEM->remove($path . DIRECTORY_SEPARATOR . 'ncc'); } - if($NCC_FILESYSTEM->exists([$path])) + if($NCC_FILESYSTEM->exists($path)) { - file_put_contents($path . DIRECTORY_SEPARATOR . 'ncc', $executable_shortcut); - $NCC_FILESYSTEM->chmod([$path . DIRECTORY_SEPARATOR . 'ncc'], 0755); + try + { + IO::fwrite($path . DIRECTORY_SEPARATOR . 'ncc', $executable_shortcut); + break; + } + catch (Exception $e) + { + Console::outException($e->getMessage(), $e, 1); + return; + } } } // Register the ncc extension Console::out('Registering extension'); - $extension_shortcut = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . 'extension'); + try + { + $extension_shortcut = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . 'extension'); + } + catch(Exception $e) + { + Console::outError($e->getMessage(), true, 1); + return; + } $extension_shortcut = str_ireplace('%ncc_install', $NCC_INSTALL_PATH, $extension_shortcut); // Remove all the old extensions first. /** * @param string $path - * @param Filesystem $NCC_FILESYSTEM + * @param Filesystem $filesystem * @param string $extension_shortcut * @return bool */ - function install_extension(string $path, Filesystem $NCC_FILESYSTEM, string $extension_shortcut): bool + function install_extension(string $path, Filesystem $filesystem, string $extension_shortcut): bool { - if (file_exists($path . DIRECTORY_SEPARATOR . 'ncc')) + if ($filesystem->exists($path . DIRECTORY_SEPARATOR . 'ncc')) { - $NCC_FILESYSTEM->remove($path . DIRECTORY_SEPARATOR . 'ncc'); + $filesystem->remove($path . DIRECTORY_SEPARATOR . 'ncc'); } - file_put_contents($path . DIRECTORY_SEPARATOR . 'ncc', $extension_shortcut); - $NCC_FILESYSTEM->chmod([$path . DIRECTORY_SEPARATOR . 'ncc'], 0755); + try + { + IO::fwrite($path . DIRECTORY_SEPARATOR . 'ncc', $extension_shortcut); + } + catch (\ncc\Exceptions\IOException $e) + { + Console::outException($e->getMessage(), $e, 1); + return false; + } - if (file_exists($path . DIRECTORY_SEPARATOR . 'ncc')) { + if ($filesystem->exists($path . DIRECTORY_SEPARATOR . 'ncc')) + { return true; } @@ -745,7 +776,15 @@ Console::out('Updating ncc.yaml'); } - file_put_contents($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml', Yaml::dump($config_obj)); + try + { + IO::fwrite($NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'ncc.yaml', Yaml::dump($config_obj)); + } + catch (\ncc\Exceptions\IOException $e) + { + Console::outException($e->getMessage(), $e, 1); + return; + } Console::out('NCC version: ' . NCC_VERSION_NUMBER . ' has been successfully installed'); Console::out('For licensing information see \'' . $NCC_INSTALL_PATH . DIRECTORY_SEPARATOR . 'LICENSE\' or run \'ncc help --license\''); diff --git a/src/ncc/Abstracts/ConstantReferences.php b/src/ncc/Abstracts/ConstantReferences.php new file mode 100644 index 0000000..43cf7ae --- /dev/null +++ b/src/ncc/Abstracts/ConstantReferences.php @@ -0,0 +1,14 @@ +""|]*[%])|([a-zA-Z][:])|(\\\\))((\\\\{1})|((\\\\{1})[^\\\\]([^\/:*?<>""|]*))+)$/m'; const ConstantName = '/^([^\x00-\x7F]|[\w_\ \.\+\-]){2,64}$/'; + + const ExecutionPolicyName = '/^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/m'; } \ No newline at end of file diff --git a/src/ncc/Abstracts/Runners.php b/src/ncc/Abstracts/Runners.php new file mode 100644 index 0000000..5a0e052 --- /dev/null +++ b/src/ncc/Abstracts/Runners.php @@ -0,0 +1,8 @@ +Project->Compiler->Extension)) - { - case CompilerExtensions::PHP: - /** @var CompilerInterface $Compiler */ - $Compiler = new Compiler($ProjectConfiguration); - break; - - default: - Console::outError('The extension '. $ProjectConfiguration->Project->Compiler->Extension . ' is not supported', true, 1); - return; - } - - $build_configuration = BuildConfigurationValues::DefaultConfiguration; - - if(isset($args['config'])) - { - $build_configuration = $args['config']; - } - - // Auto-resolve to the default configuration if `default` is used or not specified - if($build_configuration == BuildConfigurationValues::DefaultConfiguration) - { - $build_configuration = $ProjectConfiguration->Build->DefaultConfiguration; - } - - try - { - $ProjectConfiguration->Build->getBuildConfiguration($build_configuration); - } - catch (BuildConfigurationNotFoundException $e) - { - Console::outException('The build configuration \'' . $build_configuration . '\' does not exist in the project configuration', $e, 1); - return; - } - - Console::out( - ' ===== BUILD INFO ===== ' . PHP_EOL . - ' Package Name: ' . $ProjectConfiguration->Assembly->Package . PHP_EOL . - ' Version: ' . $ProjectConfiguration->Assembly->Version . PHP_EOL . - ' Compiler Extension: ' . $ProjectConfiguration->Project->Compiler->Extension . PHP_EOL . - ' Compiler Version: ' . $ProjectConfiguration->Project->Compiler->MaximumVersion . ' - ' . $ProjectConfiguration->Project->Compiler->MinimumVersion . PHP_EOL . - ' Build Configuration: ' . $build_configuration . PHP_EOL - ); - - Console::out('Preparing package'); - - try - { - $Compiler->prepare($project_path, $build_configuration); + $ProjectManager = new ProjectManager($project_path); + $ProjectManager->load(); } catch (Exception $e) { - Console::outException('The package preparation process failed', $e, 1); + Console::outException('Failed to load Project Configuration (project.json)', $e, 1); return; } - Console::out('Compiling package'); - + // Build the project try { - $Compiler->build($project_path); + $build_configuration = BuildConfigurationValues::DefaultConfiguration; + if(isset($args['config'])) + { + $build_configuration = $args['config']; + } + + $output = $ProjectManager->build($build_configuration); + + Console::out('Successfully built ' . $output); + exit(0); } catch (Exception $e) { - Console::outException('Build Failed', $e, 1); + Console::outException('Failed to build project', $e, 1); return; } - exit(0); } /** diff --git a/src/ncc/CLI/Functions.php b/src/ncc/CLI/Functions.php deleted file mode 100644 index dceff6e..0000000 --- a/src/ncc/CLI/Functions.php +++ /dev/null @@ -1,31 +0,0 @@ - 0) - { - $out = str_pad($out, $padding, $pad_string, $pad_type); - } - - if($eol) - { - $out = $out . PHP_EOL; - } - - print($out); - } - } \ No newline at end of file diff --git a/src/ncc/CLI/Main.php b/src/ncc/CLI/Main.php index c8fe242..a2a269f 100644 --- a/src/ncc/CLI/Main.php +++ b/src/ncc/CLI/Main.php @@ -1,8 +1,11 @@ getMessage() . ' (Code: ' . $e->getCode() . ')', $e, 1); + Console::outException($e->getMessage() . ' (Code: ' . $e->getCode() . ')', $e, 1); exit(1); } } } + /** + * @return mixed + */ + public static function getArgs() + { + return self::$args; + } + + /** + * @return string + */ + public static function getLogLevel(): string + { + if(self::$log_level == null) + self::$log_level = LogLevel::Info; + return self::$log_level; + } + } \ No newline at end of file diff --git a/src/ncc/CLI/PackageManagerMenu.php b/src/ncc/CLI/PackageManagerMenu.php new file mode 100644 index 0000000..319eac9 --- /dev/null +++ b/src/ncc/CLI/PackageManagerMenu.php @@ -0,0 +1,345 @@ +getInstalledPackages(); + } + catch (Exception $e) + { + unset($e); + Console::out('No packages installed'); + exit(0); + } + + foreach($installed_packages as $package => $versions) + { + if(count($versions) == 0) + { + continue; + } + + foreach($versions as $version) + { + try + { + $package_version = $package_manager->getPackageVersion($package, $version); + if($package_version == null) + throw new Exception(); + + Console::out(sprintf('%s==%s (%s)', + Console::formatColor($package, ConsoleColors::LightGreen), + Console::formatColor($version, ConsoleColors::LightMagenta), + $package_manager->getPackageVersion($package, $version)->Compiler->Extension + )); + } + catch(Exception $e) + { + unset($e); + Console::out(sprintf('%s==%s', + Console::formatColor($package, ConsoleColors::LightGreen), + Console::formatColor($version, ConsoleColors::LightMagenta) + )); + } + } + } + } + + /** + * @param $args + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + */ + private static function installPackage($args): void + { + $path = ($args['path'] ?? $args['p']); + $package_manager = new PackageManager(); + + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Insufficient permission to install packages'); + + if(!file_exists($path) || !is_file($path) || !is_readable($path)) + throw new FileNotFoundException('The specified file \'' . $path .' \' does not exist or is not readable.'); + + $user_confirmation = false; + if(isset($args['y']) || isset($args['Y'])) + { + $user_confirmation = (bool)($args['y'] ?? $args['Y']); + } + + try + { + $package = Package::load($path); + } + catch(Exception $e) + { + Console::outException('Error while loading package', $e, 1); + return; + } + + Console::out('Package installation details' . PHP_EOL); + if(!is_null($package->Assembly->UUID)) + Console::out(' UUID: ' . Console::formatColor($package->Assembly->UUID, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Package)) + Console::out(' Package: ' . Console::formatColor($package->Assembly->Package, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Name)) + Console::out(' Name: ' . Console::formatColor($package->Assembly->Name, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Version)) + Console::out(' Version: ' . Console::formatColor($package->Assembly->Version, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Description)) + Console::out(' Description: ' . Console::formatColor($package->Assembly->Description, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Product)) + Console::out(' Product: ' . Console::formatColor($package->Assembly->Product, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Company)) + Console::out(' Company: ' . Console::formatColor($package->Assembly->Company, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Copyright)) + Console::out(' Copyright: ' . Console::formatColor($package->Assembly->Copyright, ConsoleColors::LightGreen)); + if(!is_null($package->Assembly->Trademark)) + Console::out(' Trademark: ' . Console::formatColor($package->Assembly->Trademark, ConsoleColors::LightGreen)); + Console::out((string)null); + + if(count($package->Dependencies) > 0) + { + $dependencies = []; + foreach($package->Dependencies as $dependency) + { + $dependencies[] = sprintf('%s v%s', + Console::formatColor($dependency->Name, ConsoleColors::Green), + Console::formatColor($dependency->Version, ConsoleColors::LightMagenta) + ); + } + + Console::out('The following dependencies will be installed:'); + Console::out(sprintf(' %s', implode(', ', $dependencies)) . PHP_EOL); + } + + Console::out(sprintf('Extension: %s', + Console::formatColor($package->Header->CompilerExtension->Extension, ConsoleColors::Green) + )); + + if($package->Header->CompilerExtension->MaximumVersion !== null) + Console::out(sprintf('Maximum Version: %s', + Console::formatColor($package->Header->CompilerExtension->MaximumVersion, ConsoleColors::LightMagenta) + )); + + if($package->Header->CompilerExtension->MinimumVersion !== null) + Console::out(sprintf('Minimum Version: %s', + Console::formatColor($package->Header->CompilerExtension->MinimumVersion, ConsoleColors::LightMagenta) + )); + + if(!$user_confirmation) + $user_confirmation = Console::getBooleanInput(sprintf('Do you want to install %s', $package->Assembly->Package)); + + if($user_confirmation) + { + try + { + $package_manager->install($path); + } + catch(Exception $e) + { + Console::outException('Installation Failed', $e, 1); + } + + return; + } + + Console::outError('User cancelled installation', true, 1); + } + + /** + * Uninstalls a version of a package or all versions of a package + * + * @param $args + * @return void + * @throws VersionNotFoundException + */ + private static function uninstallPackage($args): void + { + $selected_package = ($args['package'] ?? $args['pkg']); + $selected_version = null; + if(isset($args['v'])) + $selected_version = $args['v']; + if(isset($args['version'])) + $selected_version = $args['version']; + + $user_confirmation = null; + // For undefined array key warnings + if(isset($args['y']) || isset($args['Y'])) + $user_confirmation = (bool)($args['y'] ?? $args['Y']); + + if($selected_package == null) + Console::outError('Missing argument \'package\'', true, 1); + + $package_manager = new PackageManager(); + + try + { + $package_entry = $package_manager->getPackage($selected_package); + } + catch (PackageLockException $e) + { + Console::outException('PackageLock error', $e, 1); + return; + } + + $version_entry = null; + if($version_entry !== null && $package_entry !== null) + /** @noinspection PhpUnhandledExceptionInspection */ + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $version_entry = $package_entry->getVersion($version_entry, false); + + if($package_entry == null) + { + Console::outError(sprintf('Package "%s" is not installed', $selected_package), true, 1); + return; + } + + if($version_entry == null & $selected_version !== null) + { + Console::outError(sprintf('Package "%s==%s" is not installed', $selected_package, $selected_version), true, 1); + return; + } + + if($user_confirmation == null) + { + if($selected_version !== null) + { + if(!Console::getBooleanInput(sprintf('Do you want to uninstall %s==%s', $selected_package, $selected_version))) + { + Console::outError('User cancelled operation', true, 1); + return; + } + } + else + { + if(!Console::getBooleanInput(sprintf('Do you want to uninstall all versions of %s', $selected_package))) + { + Console::outError('User cancelled operation', true, 1); + return; + } + } + } + + try + { + if($selected_version !== null) + { + $package_manager->uninstallPackageVersion($selected_package, $selected_version); + } + else + { + $package_manager->uninstallPackage($selected_package); + } + } + catch(Exception $e) + { + Console::outException('Uninstallation failed', $e, 1); + return; + } + } + + /** + * Displays the main options section + * + * @return void + */ + private static function displayOptions(): void + { + $options = [ + new CliHelpSection(['help'], 'Displays this help menu about the value command'), + new CliHelpSection(['install', '--path', '-p'], 'Installs a specified NCC package file'), + new CliHelpSection(['list'], 'Lists all installed packages on the system'), + ]; + + $options_padding = Functions::detectParametersPadding($options) + 4; + + Console::out('Usage: ncc install {command} [options]'); + Console::out('Options:' . PHP_EOL); + foreach($options as $option) + { + Console::out(' ' . $option->toString($options_padding)); + } + } + } \ No newline at end of file diff --git a/src/ncc/Classes/NccExtension/ConstantCompiler.php b/src/ncc/Classes/NccExtension/ConstantCompiler.php new file mode 100644 index 0000000..5ab4597 --- /dev/null +++ b/src/ncc/Classes/NccExtension/ConstantCompiler.php @@ -0,0 +1,129 @@ +Name, $input); + $input = str_replace(AssemblyConstants::AssemblyPackage, $assembly->Package, $input); + $input = str_replace(AssemblyConstants::AssemblyDescription, $assembly->Description, $input); + $input = str_replace(AssemblyConstants::AssemblyCompany, $assembly->Company, $input); + $input = str_replace(AssemblyConstants::AssemblyProduct, $assembly->Product, $input); + $input = str_replace(AssemblyConstants::AssemblyCopyright, $assembly->Copyright, $input); + $input = str_replace(AssemblyConstants::AssemblyTrademark, $assembly->Trademark, $input); + $input = str_replace(AssemblyConstants::AssemblyVersion, $assembly->Version, $input); + $input = str_replace(AssemblyConstants::AssemblyUid, $assembly->UUID, $input); + + return $input; + } + + /** + * Compiles build constants about the NCC build (Usually used during compiling time) + * + * @param string|null $input + * @return string|null + * @noinspection PhpUnnecessaryLocalVariableInspection + */ + public static function compileBuildConstants(?string $input): ?string + { + if($input == null) + return null; + + $input = str_replace(BuildConstants::CompileTimestamp, time(), $input); + $input = str_replace(BuildConstants::NccBuildVersion, NCC_VERSION_NUMBER, $input); + $input = str_replace(BuildConstants::NccBuildFlags, implode(' ', NCC_VERSION_FLAGS), $input); + $input = str_replace(BuildConstants::NccBuildBranch, NCC_VERSION_BRANCH, $input); + + return $input; + } + + /** + * Compiles installation constants (Usually used during compiling time) + * + * @param string|null $input + * @param InstallationPaths $installationPaths + * @return string|null + * @noinspection PhpUnnecessaryLocalVariableInspection + */ + public static function compileInstallConstants(?string $input, InstallationPaths $installationPaths): ?string + { + if($input == null) + return null; + + $input = str_replace(InstallConstants::InstallationPath, $installationPaths->getInstallationPath(), $input); + $input = str_replace(InstallConstants::BinPath, $installationPaths->getBinPath(), $input); + $input = str_replace(InstallConstants::SourcePath, $installationPaths->getSourcePath(), $input); + $input = str_replace(InstallConstants::DataPath, $installationPaths->getDataPath(), $input); + + return $input; + } + + /** + * Compiles DateTime constants from a Unix Timestamp + * + * @param string|null $input + * @param int $timestamp + * @return string|null + * @noinspection PhpUnnecessaryLocalVariableInspection + */ + public static function compileDateTimeConstants(?string $input, int $timestamp): ?string + { + if($input == null) + return null; + + $input = str_replace(DateTimeConstants::d, date('d', $timestamp), $input); + $input = str_replace(DateTimeConstants::D, date('D', $timestamp), $input); + $input = str_replace(DateTimeConstants::j, date('j', $timestamp), $input); + $input = str_replace(DateTimeConstants::l, date('l', $timestamp), $input); + $input = str_replace(DateTimeConstants::N, date('N', $timestamp), $input); + $input = str_replace(DateTimeConstants::S, date('S', $timestamp), $input); + $input = str_replace(DateTimeConstants::w, date('w', $timestamp), $input); + $input = str_replace(DateTimeConstants::z, date('z', $timestamp), $input); + $input = str_replace(DateTimeConstants::W, date('W', $timestamp), $input); + $input = str_replace(DateTimeConstants::F, date('F', $timestamp), $input); + $input = str_replace(DateTimeConstants::m, date('m', $timestamp), $input); + $input = str_replace(DateTimeConstants::M, date('M', $timestamp), $input); + $input = str_replace(DateTimeConstants::n, date('n', $timestamp), $input); + $input = str_replace(DateTimeConstants::t, date('t', $timestamp), $input); + $input = str_replace(DateTimeConstants::L, date('L', $timestamp), $input); + $input = str_replace(DateTimeConstants::o, date('o', $timestamp), $input); + $input = str_replace(DateTimeConstants::Y, date('Y', $timestamp), $input); + $input = str_replace(DateTimeConstants::y, date('y', $timestamp), $input); + $input = str_replace(DateTimeConstants::a, date('a', $timestamp), $input); + $input = str_replace(DateTimeConstants::A, date('A', $timestamp), $input); + $input = str_replace(DateTimeConstants::B, date('B', $timestamp), $input); + $input = str_replace(DateTimeConstants::g, date('g', $timestamp), $input); + $input = str_replace(DateTimeConstants::G, date('G', $timestamp), $input); + $input = str_replace(DateTimeConstants::h, date('h', $timestamp), $input); + $input = str_replace(DateTimeConstants::H, date('H', $timestamp), $input); + $input = str_replace(DateTimeConstants::i, date('i', $timestamp), $input); + $input = str_replace(DateTimeConstants::s, date('s', $timestamp), $input); + $input = str_replace(DateTimeConstants::c, date('c', $timestamp), $input); + $input = str_replace(DateTimeConstants::r, date('r', $timestamp), $input); + $input = str_replace(DateTimeConstants::u, date('u', $timestamp), $input); + + return $input; + } + } \ No newline at end of file diff --git a/src/ncc/Classes/NccExtension/PackageCompiler.php b/src/ncc/Classes/NccExtension/PackageCompiler.php new file mode 100644 index 0000000..8ac9bae --- /dev/null +++ b/src/ncc/Classes/NccExtension/PackageCompiler.php @@ -0,0 +1,307 @@ +getProjectConfiguration(); + + if(Main::getLogLevel() !== null && Resolver::checkLogLevel(LogLevel::Debug, Main::getLogLevel())) + { + foreach($configuration->Assembly->toArray() as $prop => $value) + Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a'))); + foreach($configuration->Project->Compiler->toArray() as $prop => $value) + Console::outDebug(sprintf('compiler.%s: %s', $prop, ($value ?? 'n/a'))); + } + + // Select the correct compiler for the specified extension + /** @noinspection PhpSwitchCanBeReplacedWithMatchExpressionInspection */ + switch(strtolower($configuration->Project->Compiler->Extension)) + { + case CompilerExtensions::PHP: + /** @var CompilerInterface $Compiler */ + $Compiler = new Compiler($configuration, $manager->getProjectPath()); + break; + + default: + throw new UnsupportedCompilerExtensionException('The compiler extension \'' . $configuration->Project->Compiler->Extension . '\' is not supported'); + } + + $build_configuration = $configuration->Build->getBuildConfiguration($build_configuration)->Name; + $Compiler->prepare($build_configuration); + $Compiler->build(); + + return PackageCompiler::writePackage( + $manager->getProjectPath(), $Compiler->getPackage(), $configuration, $build_configuration + ); + } + + /** + * Compiles the execution policies of the package + * + * @param string $path + * @param ProjectConfiguration $configuration + * @return array + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws UnsupportedRunnerException + */ + public static function compileExecutionPolicies(string $path, ProjectConfiguration $configuration): array + { + if(count($configuration->ExecutionPolicies) == 0) + return []; + + Console::out('Compiling Execution Policies'); + $total_items = count($configuration->ExecutionPolicies); + $execution_units = []; + $processed_items = 0; + + /** @var ProjectConfiguration\ExecutionPolicy $policy */ + foreach($configuration->ExecutionPolicies as $policy) + { + if($total_items > 5) + { + Console::inlineProgressBar($processed_items, $total_items); + } + + $unit_path = Functions::correctDirectorySeparator($path . $policy->Execute->Target); + $execution_units[] = Functions::compileRunner($unit_path, $policy); + } + + if(ncc::cliMode() && $total_items > 5) + print(PHP_EOL); + + return $execution_units; + } + + /** + * Writes the finished package to disk, returns the output path + * + * @param string $path + * @param Package $package + * @param ProjectConfiguration $configuration + * @param string $build_configuration + * @return string + * @throws BuildConfigurationNotFoundException + * @throws BuildException + * @throws IOException + */ + public static function writePackage(string $path, Package $package, ProjectConfiguration $configuration, string $build_configuration=BuildConfigurationValues::DefaultConfiguration): string + { + // Write the package to disk + $FileSystem = new Filesystem(); + $BuildConfiguration = $configuration->Build->getBuildConfiguration($build_configuration); + if($FileSystem->exists($path . $BuildConfiguration->OutputPath)) + { + try + { + $FileSystem->remove($path . $BuildConfiguration->OutputPath); + } + catch(\ncc\ThirdParty\Symfony\Filesystem\Exception\IOException $e) + { + throw new BuildException('Cannot delete directory \'' . $path . $BuildConfiguration->OutputPath . '\', ' . $e->getMessage(), $e); + } + } + + // Finally write the package to the disk + $FileSystem->mkdir($path . $BuildConfiguration->OutputPath); + $output_file = $path . $BuildConfiguration->OutputPath . DIRECTORY_SEPARATOR . $package->Assembly->Package . '.ncc'; + $FileSystem->touch($output_file); + + try + { + $package->save($output_file); + } + catch(Exception $e) + { + throw new IOException('Cannot write to output file', $e); + } + + return $output_file; + } + + /** + * Compiles the special formatted constants + * + * @param Package $package + * @param int $timestamp + * @return array + */ + public static function compileRuntimeConstants(Package $package, int $timestamp): array + { + $compiled_constants = []; + + foreach($package->Header->RuntimeConstants as $name => $value) + { + $compiled_constants[$name] = self::compileConstants($value, [ + ConstantReferences::Assembly => $package->Assembly, + ConstantReferences::DateTime => $timestamp, + ConstantReferences::Build => null + ]); + } + + return $compiled_constants; + } + + /** + * Compiles the constants in the package object + * + * @param Package $package + * @param array $refs + * @return void + */ + public static function compilePackageConstants(Package &$package, array $refs): void + { + if($package->Assembly !== null) + { + $assembly = []; + foreach($package->Assembly->toArray() as $key => $value) + { + $assembly[$key] = self::compileConstants($value, $refs); + } + $package->Assembly = Assembly::fromArray($assembly); + unset($assembly); + } + + if($package->ExecutionUnits !== null && count($package->ExecutionUnits) > 0) + { + $units = []; + foreach($package->ExecutionUnits as $executionUnit) + { + $units[] = self::compileExecutionUnitConstants($executionUnit, $refs); + } + $package->ExecutionUnits = $units; + unset($units); + } + } + + /** + * Compiles the constants in a given execution unit + * + * @param Package\ExecutionUnit $unit + * @param array $refs + * @return Package\ExecutionUnit + */ + public static function compileExecutionUnitConstants(Package\ExecutionUnit $unit, array $refs): Package\ExecutionUnit + { + $unit->ExecutionPolicy->Message = self::compileConstants($unit->ExecutionPolicy->Message, $refs); + + if($unit->ExecutionPolicy->ExitHandlers !== null) + { + if($unit->ExecutionPolicy->ExitHandlers->Success !== null) + { + $unit->ExecutionPolicy->ExitHandlers->Success->Message = self::compileConstants($unit->ExecutionPolicy->ExitHandlers->Success->Message, $refs); + } + + if($unit->ExecutionPolicy->ExitHandlers->Error !== null) + { + $unit->ExecutionPolicy->ExitHandlers->Error->Message = self::compileConstants($unit->ExecutionPolicy->ExitHandlers->Error->Message, $refs); + } + + if($unit->ExecutionPolicy->ExitHandlers->Warning !== null) + { + $unit->ExecutionPolicy->ExitHandlers->Warning->Error = self::compileConstants($unit->ExecutionPolicy->ExitHandlers->Warning->Message, $refs); + } + } + + if($unit->ExecutionPolicy->Execute !== null) + { + if($unit->ExecutionPolicy->Execute->Target !== null) + { + $unit->ExecutionPolicy->Execute->Target = self::compileConstants($unit->ExecutionPolicy->Execute->Target, $refs); + } + + if($unit->ExecutionPolicy->Execute->WorkingDirectory !== null) + { + $unit->ExecutionPolicy->Execute->WorkingDirectory = self::compileConstants($unit->ExecutionPolicy->Execute->WorkingDirectory, $refs); + } + + if($unit->ExecutionPolicy->Execute->Options !== null && count($unit->ExecutionPolicy->Execute->Options) > 0) + { + $options = []; + foreach($unit->ExecutionPolicy->Execute->Options as $key=>$value) + { + $options[self::compileConstants($key, $refs)] = self::compileConstants($value, $refs); + } + $unit->ExecutionPolicy->Execute->Options = $options; + } + } + + return $unit; + } + + /** + * Compiles multiple types of constants + * + * @param string|null $value + * @param array $refs + * @return string|null + */ + public static function compileConstants(?string $value, array $refs): ?string + { + if($value == null) + return null; + + if(isset($refs[ConstantReferences::Assembly])) + $value = ConstantCompiler::compileAssemblyConstants($value, $refs[ConstantReferences::Assembly]); + + if(isset($refs[ConstantReferences::Build])) + $value = ConstantCompiler::compileBuildConstants($value); + + if(isset($refs[ConstantReferences::DateTime])) + $value = ConstantCompiler::compileDateTimeConstants($value, $refs[ConstantReferences::DateTime]); + + if(isset($refs[ConstantReferences::Install])) + $value = ConstantCompiler::compileInstallConstants($value, $refs[ConstantReferences::Install]); + + return $value; + } + } \ No newline at end of file diff --git a/src/ncc/Classes/NccExtension/Runner.php b/src/ncc/Classes/NccExtension/Runner.php new file mode 100644 index 0000000..2133137 --- /dev/null +++ b/src/ncc/Classes/NccExtension/Runner.php @@ -0,0 +1,47 @@ +addUnit($package, $version, $unit, true); + $ExecutionPointerManager->executeUnit($package, $version, $unit->ExecutionPolicy->Name); + $ExecutionPointerManager->cleanTemporaryUnits();; + } + } \ No newline at end of file diff --git a/src/ncc/Classes/PackageParser.php b/src/ncc/Classes/PackageParser.php deleted file mode 100644 index 1d54da1..0000000 --- a/src/ncc/Classes/PackageParser.php +++ /dev/null @@ -1,42 +0,0 @@ -PackagePath = $path; - $this->parseFile(); - } - - private function parseFile() - { - if(file_exists($this->PackagePath) == false) - { - throw new FileNotFoundException('The given package path \'' . $this->PackagePath . '\' does not exist'); - } - - if(is_file($this->PackagePath) == false) - { - throw new FileNotFoundException('The given package path \'' . $this->PackagePath . '\' is not a file'); - } - - $file_handler = fopen($this->PackagePath, 'rb'); - $header = fread($file_handler, 14); - } - } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/AutoloaderGenerator.php b/src/ncc/Classes/PhpExtension/AutoloaderGenerator.php deleted file mode 100644 index 2f94c89..0000000 --- a/src/ncc/Classes/PhpExtension/AutoloaderGenerator.php +++ /dev/null @@ -1,120 +0,0 @@ -project = $project; - } - - /** - * Processes the project and generates the autoloader source code. - * - * @param string $src - * @param string $output - * @param bool $static - * @return string - * @throws AutoloadGeneratorException - * @throws CollectorException - * @throws Exception - * @throws NoUnitsFoundException - */ - public function generateAutoload(string $src, string $output, bool $static=false): string - { - // Construct configuration - $configuration = new Config([$src]); - $configuration->setFollowSymlinks(false); // Don't follow symlinks, it won't work on some systems. - $configuration->setOutputFile($output); - $configuration->setTrusting(false); // Paranoid - // Official PHP file extensions that are missing from the default configuration (whatever) - $configuration->setInclude(ComponentFileExtensions::Php); - - // Construct factory - $factory = new Factory(); - $factory->setConfig($configuration); - - // Create Collector - $result = self::runCollector($factory, $configuration); - - // Exception raises when there are no files in the project that can be processed by the autoloader - if(!$result->hasUnits()) - { - throw new NoUnitsFoundException('No units were found in the project'); - } - - if(!$result->hasDuplicates()) - { - foreach($result->getDuplicates() as $unit => $files) - { - Console::outWarning((count($files) -1). ' duplicate unit(s) detected in the project: ' . $unit); - } - } - - $template = @file_get_contents($configuration->getTemplate()); - if ($template === false) - { - throw new AutoloadGeneratorException("Failed to read the template file '" . $configuration->getTemplate() . "'"); - } - - $builder = $factory->getRenderer($result); - return $builder->render($template); - } - - /** - * Iterates through the target directories through the collector and returns the collector results. - * - * @param Factory $factory - * @param Config $config - * @return CollectorResult - * @throws CollectorException - * @throws Exception - */ - private static function runCollector(Factory $factory, Config $config): CollectorResult - { - $collector = $factory->getCollector(); - foreach($config->getDirectories() as $directory) - { - if(is_dir($directory)) - { - $scanner = $factory->getScanner()->getIterator($directory); - $collector->processDirectory($scanner); - unset($scanner); - } - else - { - $file = new SplFileInfo($directory); - $filter = $factory->getFilter(new ArrayIterator(array($file))); - foreach($filter as $file) - { - $collector->processFile($file); - } - } - } - - return $collector->getResult(); - } - - } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/Compiler.php b/src/ncc/Classes/PhpExtension/Compiler.php index 4257002..d6df577 100644 --- a/src/ncc/Classes/PhpExtension/Compiler.php +++ b/src/ncc/Classes/PhpExtension/Compiler.php @@ -8,22 +8,26 @@ use FilesystemIterator; use ncc\Abstracts\ComponentFileExtensions; use ncc\Abstracts\ComponentDataType; + use ncc\Abstracts\ConstantReferences; use ncc\Abstracts\Options\BuildConfigurationValues; + use ncc\Classes\NccExtension\PackageCompiler; + use ncc\Exceptions\AccessDeniedException; use ncc\Exceptions\BuildConfigurationNotFoundException; use ncc\Exceptions\BuildException; + use ncc\Exceptions\FileNotFoundException; + use ncc\Exceptions\IOException; use ncc\Exceptions\PackagePreparationFailedException; + use ncc\Exceptions\UnsupportedRunnerException; use ncc\Interfaces\CompilerInterface; use ncc\ncc; use ncc\Objects\Package; use ncc\Objects\ProjectConfiguration; - use ncc\ThirdParty\nikic\PhpParser\Error; use ncc\ThirdParty\nikic\PhpParser\ParserFactory; - use ncc\ThirdParty\Symfony\Filesystem\Exception\IOException; - use ncc\ThirdParty\Symfony\Filesystem\Filesystem; use ncc\ThirdParty\theseer\DirectoryScanner\DirectoryScanner; use ncc\Utilities\Base64; use ncc\Utilities\Console; use ncc\Utilities\Functions; + use ncc\Utilities\IO; use SplFileInfo; class Compiler implements CompilerInterface @@ -39,28 +43,30 @@ private $package; /** - * @var ProjectConfiguration\BuildConfiguration|null + * @var string */ - private $selected_build_configuration; + private $path; /** * @param ProjectConfiguration $project + * @param string $path */ - public function __construct(ProjectConfiguration $project) + public function __construct(ProjectConfiguration $project, string $path) { $this->project = $project; + $this->path = $path; } /** * Prepares the PHP package by generating the Autoloader and detecting all components & resources * This function must be called before calling the build function, otherwise the operation will fail * - * @param string $path * @param string $build_configuration * @return void * @throws PackagePreparationFailedException + * @throws BuildConfigurationNotFoundException */ - public function prepare(string $path, string $build_configuration=BuildConfigurationValues::DefaultConfiguration): void + public function prepare(string $build_configuration=BuildConfigurationValues::DefaultConfiguration): void { try { @@ -72,40 +78,29 @@ throw new PackagePreparationFailedException($e->getMessage(), $e); } - // Auto-select the default build configuration - if($build_configuration == BuildConfigurationValues::DefaultConfiguration) - { - $build_configuration = $this->project->Build->DefaultConfiguration; - } - // Select the build configuration - try - { - $this->selected_build_configuration = $this->project->Build->getBuildConfiguration($build_configuration); - } - catch (BuildConfigurationNotFoundException $e) - { - throw new PackagePreparationFailedException($e->getMessage(), $e); - } + $selected_build_configuration = $this->project->Build->getBuildConfiguration($build_configuration); // Create the package object $this->package = new Package(); $this->package->Assembly = $this->project->Assembly; $this->package->Dependencies = $this->project->Build->Dependencies; + $this->package->MainExecutionPolicy = $this->project->Build->Main; // Add both the defined constants from the build configuration and the global constants. // Global constants are overridden - $this->package->Header->RuntimeConstants = array_merge($this->selected_build_configuration->DefineConstants, $this->package->Header->RuntimeConstants); - $this->package->Header->RuntimeConstants = array_merge($this->project->Build->DefineConstants, $this->package->Header->RuntimeConstants); + $this->package->Header->RuntimeConstants = []; + $this->package->Header->RuntimeConstants = array_merge( + $selected_build_configuration->DefineConstants, + $this->project->Build->DefineConstants, + $this->package->Header->RuntimeConstants + ); $this->package->Header->CompilerExtension = $this->project->Project->Compiler; $this->package->Header->CompilerVersion = NCC_VERSION_NUMBER; - if(ncc::cliMode()) - { - Console::out('Scanning project files'); - Console::out('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts All rights reserved.'); - } + Console::out('Scanning project files'); + Console::out('theseer\DirectoryScanner - Copyright (c) 2009-2014 Arne Blankerts All rights reserved.'); // First scan the project files and create a file struct. $DirectoryScanner = new DirectoryScanner(); @@ -121,18 +116,12 @@ // Include file components that can be compiled $DirectoryScanner->setIncludes(ComponentFileExtensions::Php); - $DirectoryScanner->setExcludes($this->selected_build_configuration->ExcludeFiles); - - // Append trailing slash to the end of the path if it's not already there - if(substr($path, -1) !== DIRECTORY_SEPARATOR) - { - $path .= DIRECTORY_SEPARATOR; - } - - $source_path = $path . $this->project->Build->SourcePath; + $DirectoryScanner->setExcludes($selected_build_configuration->ExcludeFiles); + $source_path = $this->path . $this->project->Build->SourcePath; + // TODO: Re-implement the scanning process outside the compiler, as this is will be redundant // Scan for components first. - Console::out('Scanning for components... ', false); + Console::out('Scanning for components... '); /** @var SplFileInfo $item */ /** @noinspection PhpRedundantOptionalArgumentInspection */ foreach($DirectoryScanner($source_path, True) as $item) @@ -142,22 +131,20 @@ continue; $Component = new Package\Component(); - $Component->Name = Functions::removeBasename($item->getPathname(), $path); + $Component->Name = Functions::removeBasename($item->getPathname(), $this->path); $this->package->Components[] = $Component; + + Console::outVerbose(sprintf('found component %s', $Component->Name)); } - if(ncc::cliMode()) + if(count($this->package->Components) > 0) { - if(count($this->package->Components) > 0) - { - Console::out(count($this->package->Components) . ' component(s) found'); - } - else - { - Console::out('No components found'); - } + Console::out(count($this->package->Components) . ' component(s) found'); + } + else + { + Console::out('No components found'); } - // Clear previous excludes and includes $DirectoryScanner->setExcludes([]); @@ -165,10 +152,10 @@ // Ignore component files $DirectoryScanner->setExcludes(array_merge( - $this->selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php + $selected_build_configuration->ExcludeFiles, ComponentFileExtensions::Php )); - Console::out('Scanning for resources... ', false); + Console::out('Scanning for resources... '); /** @var SplFileInfo $item */ foreach($DirectoryScanner($source_path) as $item) { @@ -177,174 +164,185 @@ continue; $Resource = new Package\Resource(); - $Resource->Name = Functions::removeBasename($item->getPathname(), $path); + $Resource->Name = Functions::removeBasename($item->getPathname(), $this->path); $this->package->Resources[] = $Resource; - } - - if(ncc::cliMode()) - { - if(count($this->package->Resources) > 0) - { - Console::out(count($this->package->Resources) . ' resources(s) found'); - } - else - { - Console::out('No resources found'); - } - } - } - - /** - * Builds the package by parsing the AST contents of the components and resources - * - * @param string $path - * @return string - * @throws BuildException - */ - public function build(string $path): string - { - if($this->package == null) - { - throw new BuildException('The prepare() method must be called before building the package'); - } - - // Append trailing slash to the end of the path if it's not already there - if(substr($path, -1) !== DIRECTORY_SEPARATOR) - { - $path .= DIRECTORY_SEPARATOR; - } - - // Runtime variables - $components = []; - $resources = []; - $processed_items = 0; - $total_items = 0; - - if(count($this->package->Components) > 0) - { - if(ncc::cliMode()) - { - Console::out('Compiling components'); - $total_items = count($this->package->Components); - } - - // Process the components and attempt to create an AST representation of the source - foreach($this->package->Components as $component) - { - if(ncc::cliMode() && $total_items > 5) - { - Console::inlineProgressBar($processed_items, $total_items); - } - - $content = file_get_contents(Functions::correctDirectorySeparator($path . $component->Name)); - $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); - - try - { - $stmts = $parser->parse($content); - $encoded = json_encode($stmts); - - if($encoded === false) - { - $component->DataType = ComponentDataType::b64encoded; - $component->Data = Base64::encode($content); - $component->Checksum = hash('sha1', $component->Data); - } - else - { - $component->DataType = ComponentDataType::AST; - $component->Data = json_decode($encoded, true); - $component->Checksum = null; - } - } - catch(Error $e) - { - $component->DataType = ComponentDataType::b64encoded; - $component->Data = Base64::encode($content); - $component->Checksum = hash('sha1', $component->Data); - unset($e); - } - - $component->Name = str_replace($this->project->Build->SourcePath, (string)null, $component->Name); - $components[] = $component; - $processed_items += 1; - } - - if(ncc::cliMode() && $total_items > 5) - { - print(PHP_EOL); - } - - // Update the components - $this->package->Components = $components; + Console::outVerbose(sprintf('found resource %s', $Resource->Name)); } if(count($this->package->Resources) > 0) { - // Process the resources - if(ncc::cliMode()) - { - Console::out('Processing resources'); - $processed_items = 0; - $total_items = count($this->package->Resources); - } - - foreach($this->package->Resources as $resource) - { - if(ncc::cliMode() && $total_items > 5) - { - Console::inlineProgressBar($processed_items, $total_items); - } - - // Get the data and - $resource->Data = file_get_contents(Functions::correctDirectorySeparator($path . $resource->Name)); - $resource->Data = Base64::encode($resource->Data); - $resource->Checksum = hash('sha1', $resource->Data); - $resource->Name = str_replace($this->project->Build->SourcePath, (string)null, $resource->Name); - $resources[] = $resource; - } - - // Update the resources - $this->package->Resources = $resources; + Console::out(count($this->package->Resources) . ' resources(s) found'); } + else + { + Console::out('No resources found'); + } + } - if(ncc::cliMode()) + /** + * Executes the compile process in the correct order and returns the finalized Package object + * + * @return Package|null + * @throws AccessDeniedException + * @throws BuildException + * @throws FileNotFoundException + * @throws IOException + * @throws UnsupportedRunnerException + */ + public function build(): ?Package + { + $this->compileExecutionPolicies(); + $this->compileComponents(); + $this->compileResources(); + + PackageCompiler::compilePackageConstants($this->package, [ + ConstantReferences::Assembly => $this->project->Assembly, + ConstantReferences::Build => null, + ConstantReferences::DateTime => time() + ]); + + return $this->getPackage(); + } + + /** + * Compiles the resources of the package + * + * @return void + * @throws AccessDeniedException + * @throws BuildException + * @throws FileNotFoundException + * @throws IOException + */ + public function compileResources(): void + { + if($this->package == null) + throw new BuildException('The prepare() method must be called before building the package'); + + if(count($this->package->Resources) == 0) + return; + + // Process the resources + Console::out('Processing resources'); + $total_items = count($this->package->Resources); + $processed_items = 0; + $resources = []; + + foreach($this->package->Resources as $resource) { if($total_items > 5) - print(PHP_EOL); - Console::out($this->package->Assembly->Package . ' compiled successfully'); + { + Console::inlineProgressBar($processed_items, $total_items); + } + + // Get the data and + $resource->Data = IO::fread(Functions::correctDirectorySeparator($this->path . $resource->Name)); + $resource->Data = Base64::encode($resource->Data); + $resource->Name = str_replace($this->project->Build->SourcePath, (string)null, $resource->Name); + $resource->updateChecksum(); + $resources[] = $resource; + + Console::outDebug(sprintf('processed resource %s', $resource->Name)); } - // Write the package to disk - $FileSystem = new Filesystem(); + // Update the resources + $this->package->Resources = $resources; + } - if($FileSystem->exists($path . $this->selected_build_configuration->OutputPath)) + /** + * Compiles the components of the package + * + * @return void + * @throws AccessDeniedException + * @throws BuildException + * @throws FileNotFoundException + * @throws IOException + */ + public function compileComponents(): void + { + if($this->package == null) + throw new BuildException('The prepare() method must be called before building the package'); + + if(count($this->package->Components) == 0) + return; + + Console::out('Compiling components'); + $total_items = count($this->package->Components); + $processed_items = 0; + $components = []; + + // Process the components and attempt to create an AST representation of the source + foreach($this->package->Components as $component) { + if($total_items > 5) + { + Console::inlineProgressBar($processed_items, $total_items); + } + + $content = IO::fread(Functions::correctDirectorySeparator($this->path . $component->Name)); + $parser = (new ParserFactory())->create(ParserFactory::PREFER_PHP7); + try { - $FileSystem->remove($path . $this->selected_build_configuration->OutputPath); + $stmts = $parser->parse($content); + $encoded = json_encode($stmts); + unset($stmts); + + if($encoded === false) + { + $component->DataType = ComponentDataType::b64encoded; + $component->Data = Base64::encode($content); + } + else + { + $component->DataType = ComponentDataType::AST; + $component->Data = json_decode($encoded, true); + } } - catch(IOException $e) + catch(Exception $e) { - throw new BuildException('Cannot delete directory \'' . $path . $this->selected_build_configuration->OutputPath . '\', ' . $e->getMessage(), $e); + $component->DataType = ComponentDataType::b64encoded; + $component->Data = Base64::encode($content); + unset($e); } + + unset($parser); + + $component->Name = str_replace($this->project->Build->SourcePath, (string)null, $component->Name); + $component->updateChecksum(); + $components[] = $component; + $processed_items += 1; + + Console::outDebug(sprintf('processed component %s (%s)', $component->Name, $component->DataType)); } - // Finally write the package to the disk - $FileSystem->mkdir($path . $this->selected_build_configuration->OutputPath); - $output_file = $path . $this->selected_build_configuration->OutputPath . DIRECTORY_SEPARATOR . $this->package->Assembly->Package . '.ncc'; - $FileSystem->touch($output_file); - - try + if(ncc::cliMode() && $total_items > 5) { - $this->package->save($output_file); - } - catch(Exception $e) - { - throw new BuildException('Cannot write to output file', $e); + print(PHP_EOL); } - return $output_file; + // Update the components + $this->package->Components = $components; } + + /** + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws UnsupportedRunnerException + */ + public function compileExecutionPolicies(): void + { + PackageCompiler::compileExecutionPolicies($this->path, $this->project); + } + + /** + * @inheritDoc + */ + public function getPackage(): ?Package + { + return $this->package; + } + } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/Installer.php b/src/ncc/Classes/PhpExtension/Installer.php new file mode 100644 index 0000000..ade05ec --- /dev/null +++ b/src/ncc/Classes/PhpExtension/Installer.php @@ -0,0 +1,342 @@ +package = $package; + } + + /** + * Processes the given component and returns the decoded component as a string representation + * If the processed component does not result in a string representation, none will be returned. + * + * @param Component $component + * @return string|null + * @throws ComponentChecksumException + * @throws ComponentDecodeException + * @throws UnsupportedComponentTypeException + */ + public function processComponent(Package\Component $component): ?string + { + if($component->Data == null) + return null; + + if(!$component->validateChecksum()) + throw new ComponentChecksumException('Checksum validation failed for component ' . $component->Name . ', the package may be corrupted.'); + + switch($component->DataType) + { + case ComponentDataType::AST: + try + { + $stmts = $this->decodeRecursive($component->Data); + } + catch (Exception $e) + { + throw new ComponentDecodeException('Cannot decode component: ' . $component->Name . ', ' . $e->getMessage(), $e); + } + + $prettyPrinter = new Standard(); + return $prettyPrinter->prettyPrintFile($stmts); + + case ComponentDataType::b64encoded: + return Base64::decode($component->Data); + + case ComponentDataType::Plain: + return $component->Data; + + default: + throw new UnsupportedComponentTypeException('Unsupported component type \'' . $component->DataType . '\''); + } + } + + /** + * @inheritDoc + */ + public function preInstall(InstallationPaths $installationPaths): void + { + } + + /** + * @inheritDoc + */ + public function postInstall(InstallationPaths $installationPaths): void + { + $autoload_path = $installationPaths->getBinPath() . DIRECTORY_SEPARATOR . 'autoload.php'; + $autoload_src = $this->generateAutoload($installationPaths->getSourcePath(), $autoload_path); + IO::fwrite($autoload_path, $autoload_src); + } + + /** + * Processes the given resource and returns the string representation of the resource + * + * @param Package\Resource $resource + * @return string|null + * @throws ResourceChecksumException + */ + public function processResource(Package\Resource $resource): ?string + { + if(!$resource->validateChecksum()) + throw new ResourceChecksumException('Checksum validation failed for resource ' . $resource->Name . ', the package may be corrupted.'); + return Base64::decode($resource->Data); + } + + /** + * @param $value + * @return array|Comment|Node + * @throws ReflectionException + * @noinspection PhpMissingReturnTypeInspection + */ + private function decodeRecursive($value) + { + if (is_array($value)) + { + if (isset($value['nodeType'])) + { + if ($value['nodeType'] === 'Comment' || $value['nodeType'] === 'Comment_Doc') + { + return $this->decodeComment($value); + } + return $this->decodeNode($value); + } + return $this->decodeArray($value); + } + return $value; + } + + /** + * @param array $array + * @return array + * @throws ReflectionException + */ + private function decodeArray(array $array) : array + { + $decodedArray = []; + foreach ($array as $key => $value) + { + $decodedArray[$key] = $this->decodeRecursive($value); + } + return $decodedArray; + } + + /** + * @param array $value + * @return Node + * @throws ReflectionException + */ + private function decodeNode(array $value) : Node + { + $nodeType = $value['nodeType']; + if (!is_string($nodeType)) + { + throw new RuntimeException('Node type must be a string'); + } + + $reflectionClass = $this->reflectionClassFromNodeType($nodeType); + /** @var Node $node */ + $node = $reflectionClass->newInstanceWithoutConstructor(); + + if (isset($value['attributes'])) { + if (!is_array($value['attributes'])) + { + throw new RuntimeException('Attributes must be an array'); + } + + $node->setAttributes($this->decodeArray($value['attributes'])); + } + + foreach ($value as $name => $subNode) { + if ($name === 'nodeType' || $name === 'attributes') + { + continue; + } + + $node->$name = $this->decodeRecursive($subNode); + } + + return $node; + } + + /** + * @param array $value + * @return Comment + */ + private function decodeComment(array $value) : Comment + { + $className = $value['nodeType'] === 'Comment' ? Comment::class : Comment\Doc::class; + if (!isset($value['text'])) + { + throw new RuntimeException('Comment must have text'); + } + + return new $className( + $value['text'], + $value['line'] ?? -1, $value['filePos'] ?? -1, $value['tokenPos'] ?? -1, + $value['endLine'] ?? -1, $value['endFilePos'] ?? -1, $value['endTokenPos'] ?? -1 + ); + } + + /** + * @param string $nodeType + * @return ReflectionClass + * @throws ReflectionException + */ + private function reflectionClassFromNodeType(string $nodeType) : ReflectionClass + { + if (!isset($this->reflectionClassCache[$nodeType])) + { + $className = $this->classNameFromNodeType($nodeType); + $this->reflectionClassCache[$nodeType] = new ReflectionClass($className); + } + return $this->reflectionClassCache[$nodeType]; + } + + /** + * @param string $nodeType + * @return string + */ + private function classNameFromNodeType(string $nodeType) : string + { + $className = 'ncc\\ThirdParty\\nikic\\PhpParser\\Node\\' . strtr($nodeType, '_', '\\'); + if (class_exists($className)) + { + return $className; + } + + $className .= '_'; + if (class_exists($className)) + { + return $className; + } + + throw new RuntimeException("Unknown node type \"$nodeType\""); + } + + /** + * Processes the project and generates the autoloader source code. + * + * @param string $src + * @param string $output + * @return string + * @throws AccessDeniedException + * @throws CollectorException + * @throws FileNotFoundException + * @throws IOException + * @throws NoUnitsFoundException + */ + private function generateAutoload(string $src, string $output): string + { + // Construct configuration + $configuration = new Config([$src]); + $configuration->setFollowSymlinks(false); // Don't follow symlinks, it won't work on some systems. + $configuration->setTrusting(true); // Paranoid + $configuration->setOutputFile($output); + $configuration->setStaticMode(false); + // Official PHP file extensions that are missing from the default configuration (whatever) + $configuration->setInclude(ComponentFileExtensions::Php); + $configuration->setQuietMode(true); + + // Construct factory + $factory = new Factory(); + $factory->setConfig($configuration); + + // Create Collector + $result = self::runCollector($factory, $configuration); + + // Exception raises when there are no files in the project that can be processed by the autoloader + if(!$result->hasUnits()) + { + throw new NoUnitsFoundException('No units were found in the project'); + } + + $template = IO::fread($configuration->getTemplate()); + + $builder = $factory->getRenderer($result); + return $builder->render($template); + } + + /** + * Iterates through the target directories through the collector and returns the collector results. + * + * @param Factory $factory + * @param Config $config + * @return CollectorResult + * @throws CollectorException + * @throws Exception + */ + private static function runCollector(Factory $factory, Config $config): CollectorResult + { + $collector = $factory->getCollector(); + foreach($config->getDirectories() as $directory) + { + if(is_dir($directory)) + { + $scanner = $factory->getScanner()->getIterator($directory); + $collector->processDirectory($scanner); + unset($scanner); + } + else + { + $file = new SplFileInfo($directory); + $filter = $factory->getFilter(new ArrayIterator(array($file))); + foreach($filter as $file) + { + $collector->processFile($file); + } + } + } + + return $collector->getResult(); + } + } \ No newline at end of file diff --git a/src/ncc/Classes/PhpExtension/Runner.php b/src/ncc/Classes/PhpExtension/Runner.php new file mode 100644 index 0000000..fe8dbb4 --- /dev/null +++ b/src/ncc/Classes/PhpExtension/Runner.php @@ -0,0 +1,67 @@ +Execute->Target = null; + $execution_unit->ExecutionPolicy = $policy; + $execution_unit->Data = Base64::encode(IO::fread($target_file)); + + return $execution_unit; + } + + /** + * Returns the file extension to use for the target file + * + * @return string + */ + public static function getFileExtension(): string + { + return '.php'; + } + + /** + * @param ExecutionPointer $pointer + * @return Process + * @throws RunnerExecutionException + */ + public static function prepareProcess(ExecutionPointer $pointer): Process + { + $php_bin = new ExecutableFinder(); + $php_bin = $php_bin->find('php'); + if($php_bin == null) + throw new RunnerExecutionException('Cannot locate PHP executable'); + + if($pointer->ExecutionPolicy->Execute->Options !== null && count($pointer->ExecutionPolicy->Execute->Options) > 0) + return new Process(array_merge([$php_bin, $pointer->FilePointer], $pointer->ExecutionPolicy->Execute->Options)); + return new Process([$php_bin, $pointer->FilePointer]); + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/ComponentChecksumException.php b/src/ncc/Exceptions/ComponentChecksumException.php new file mode 100644 index 0000000..68bb520 --- /dev/null +++ b/src/ncc/Exceptions/ComponentChecksumException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/ComponentDecodeException.php b/src/ncc/Exceptions/ComponentDecodeException.php new file mode 100644 index 0000000..593255f --- /dev/null +++ b/src/ncc/Exceptions/ComponentDecodeException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/ExecutionUnitNotFoundException.php b/src/ncc/Exceptions/ExecutionUnitNotFoundException.php new file mode 100644 index 0000000..228f512 --- /dev/null +++ b/src/ncc/Exceptions/ExecutionUnitNotFoundException.php @@ -0,0 +1,8 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/InstallationException.php b/src/ncc/Exceptions/InstallationException.php new file mode 100644 index 0000000..1b99c67 --- /dev/null +++ b/src/ncc/Exceptions/InstallationException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/InvalidExecutionPolicyName.php b/src/ncc/Exceptions/InvalidExecutionPolicyName.php new file mode 100644 index 0000000..2699960 --- /dev/null +++ b/src/ncc/Exceptions/InvalidExecutionPolicyName.php @@ -0,0 +1,14 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/PackageAlreadyInstalledException.php b/src/ncc/Exceptions/PackageAlreadyInstalledException.php new file mode 100644 index 0000000..fbf5c49 --- /dev/null +++ b/src/ncc/Exceptions/PackageAlreadyInstalledException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/PackageLockException.php b/src/ncc/Exceptions/PackageLockException.php new file mode 100644 index 0000000..b86c20c --- /dev/null +++ b/src/ncc/Exceptions/PackageLockException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/PackageNotFoundException.php b/src/ncc/Exceptions/PackageNotFoundException.php new file mode 100644 index 0000000..88bd370 --- /dev/null +++ b/src/ncc/Exceptions/PackageNotFoundException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/PackageParsingException.php b/src/ncc/Exceptions/PackageParsingException.php new file mode 100644 index 0000000..70c1be7 --- /dev/null +++ b/src/ncc/Exceptions/PackageParsingException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/ProjectConfigurationNotFoundException.php b/src/ncc/Exceptions/ProjectConfigurationNotFoundException.php new file mode 100644 index 0000000..2897f72 --- /dev/null +++ b/src/ncc/Exceptions/ProjectConfigurationNotFoundException.php @@ -0,0 +1,13 @@ +message = $message; + $this->code = $code; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/RunnerExecutionException.php b/src/ncc/Exceptions/RunnerExecutionException.php new file mode 100644 index 0000000..e568dd1 --- /dev/null +++ b/src/ncc/Exceptions/RunnerExecutionException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/UndefinedExecutionPolicyException.php b/src/ncc/Exceptions/UndefinedExecutionPolicyException.php new file mode 100644 index 0000000..8797b5e --- /dev/null +++ b/src/ncc/Exceptions/UndefinedExecutionPolicyException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/UnsupportedComponentTypeException.php b/src/ncc/Exceptions/UnsupportedComponentTypeException.php new file mode 100644 index 0000000..ecd4c79 --- /dev/null +++ b/src/ncc/Exceptions/UnsupportedComponentTypeException.php @@ -0,0 +1,28 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Exceptions/UnsupportedRunnerException.php b/src/ncc/Exceptions/UnsupportedRunnerException.php new file mode 100644 index 0000000..830a7a1 --- /dev/null +++ b/src/ncc/Exceptions/UnsupportedRunnerException.php @@ -0,0 +1,11 @@ +message = $message; + $this->previous = $previous; + } + } \ No newline at end of file diff --git a/src/ncc/Interfaces/CompilerInterface.php b/src/ncc/Interfaces/CompilerInterface.php index 4fa160d..c098abf 100644 --- a/src/ncc/Interfaces/CompilerInterface.php +++ b/src/ncc/Interfaces/CompilerInterface.php @@ -3,23 +3,79 @@ namespace ncc\Interfaces; use ncc\Abstracts\Options\BuildConfigurationValues; + use ncc\Exceptions\AccessDeniedException; + use ncc\Exceptions\BuildException; + use ncc\Exceptions\FileNotFoundException; + use ncc\Exceptions\IOException; + use ncc\Exceptions\UnsupportedRunnerException; + use ncc\Objects\Package; + use ncc\Objects\ProjectConfiguration; interface CompilerInterface { + /** + * Public constructor + * + * @param ProjectConfiguration $project + * @param string $path + */ + public function __construct(ProjectConfiguration $project, string $path); + /** * Prepares the package for the build process, this method is called before build() * - * @param string $path The path that the project file is located in (project.json) * @param string $build_configuration The build configuration to use to build the project * @return void */ - public function prepare(string $path, string $build_configuration=BuildConfigurationValues::DefaultConfiguration): void; + public function prepare(string $build_configuration=BuildConfigurationValues::DefaultConfiguration): void; /** - * Builds the package, returns the output path of the build + * Executes the compile process in the correct order and returns the finalized Package object * - * @param string $path The path that the project file is located in (project.json) - * @return string Returns the output path of the build + * @return Package|null + * @throws AccessDeniedException + * @throws BuildException + * @throws FileNotFoundException + * @throws IOException + * @throws UnsupportedRunnerException */ - public function build(string $path): string; + public function build(): ?Package; + + /** + * Compiles the components of the package + * + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public function compileComponents(): void; + + /** + * Compiles the resources of the package + * + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public function compileResources(): void; + + /** + * Compiles the execution policies of the package + * + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws UnsupportedRunnerException + */ + public function compileExecutionPolicies(): void; + + /** + * Returns the current state of the package + * + * @return Package|null + */ + public function getPackage(): ?Package; } \ No newline at end of file diff --git a/src/ncc/Interfaces/InstallerInterface.php b/src/ncc/Interfaces/InstallerInterface.php new file mode 100644 index 0000000..75049ce --- /dev/null +++ b/src/ncc/Interfaces/InstallerInterface.php @@ -0,0 +1,59 @@ +CredentialsPath = PathFinder::getDataPath(Scopes::System) . DIRECTORY_SEPARATOR . 'credentials.store'; } @@ -51,7 +55,7 @@ * * @return void * @throws AccessDeniedException - * @throws RuntimeException + * @throws IOException */ public function constructStore(): void { @@ -68,12 +72,7 @@ $VaultObject = new Vault(); $VaultObject->Version = Versions::CredentialsStoreVersion; - if(!@file_put_contents($this->CredentialsPath, ZiProto::encode($VaultObject->toArray()))) - { - throw new RuntimeException('Cannot create file \'' . $this->CredentialsPath . '\''); - } - - chmod($this->CredentialsPath, 0600); + IO::fwrite($this->CredentialsPath, ZiProto::encode($VaultObject->toArray()), 0600); } /** @@ -81,6 +80,7 @@ * * @return Vault * @throws AccessDeniedException + * @throws IOException * @throws RuntimeException */ public function getVault(): Vault @@ -94,17 +94,15 @@ try { - $Vault = ZiProto::decode(file_get_contents($this->CredentialsPath)); + $Vault = ZiProto::decode(IO::fread($this->CredentialsPath)); } - catch(\Exception $e) + catch(Exception $e) { // TODO: Implement error-correction for corrupted credentials store. throw new RuntimeException($e->getMessage(), $e); } - $Vault = Vault::fromArray($Vault); - - return $Vault; + return Vault::fromArray($Vault); } /** @@ -113,15 +111,16 @@ * @param Vault $vault * @return void * @throws AccessDeniedException + * @throws IOException */ - public function saveVault(Vault $vault) + public function saveVault(Vault $vault): void { if(!$this->checkAccess()) { throw new AccessDeniedException('Cannot write to credentials store without system permissions'); } - file_put_contents($this->CredentialsPath, ZiProto::encode($vault->toArray())); + IO::fwrite($this->CredentialsPath, ZiProto::encode($vault->toArray()), 0600); } /** @@ -132,8 +131,9 @@ * @throws AccessDeniedException * @throws InvalidCredentialsEntryException * @throws RuntimeException + * @throws IOException */ - public function registerEntry(Vault\Entry $entry) + public function registerEntry(Vault\Entry $entry): void { if(!preg_match('/^[\w-]+$/', $entry->Alias)) { @@ -152,7 +152,7 @@ /** * @return null */ - public function getCredentialsPath() + public function getCredentialsPath(): ?string { return $this->CredentialsPath; } diff --git a/src/ncc/Managers/ExecutionPointerManager.php b/src/ncc/Managers/ExecutionPointerManager.php new file mode 100644 index 0000000..bedd2b5 --- /dev/null +++ b/src/ncc/Managers/ExecutionPointerManager.php @@ -0,0 +1,424 @@ +cleanTemporaryUnits(); + } + catch(Exception $e) + { + unset($e); + } + } + + /** + * @throws InvalidScopeException + */ + public function __construct() + { + $this->RunnerPath = PathFinder::getRunnerPath(Scopes::System); + $this->TemporaryUnits = []; + } + + /** + * Deletes all temporary files and directories + * + * @return void + */ + public function cleanTemporaryUnits(): void + { + if(count($this->TemporaryUnits) == 0) + return; + + try + { + foreach($this->TemporaryUnits as $datum) + { + $this->removeUnit($datum['package'], $datum['version'], $datum['name']); + } + } + catch(Exception $e) + { + unset($e); + } + } + + /** + * Calculates the Package ID for the execution pointers + * + * @param string $package + * @param string $version + * @return string + */ + private function getPackageId(string $package, string $version): string + { + return hash('haval128,4', $package . $version); + } + + /** + * Adds a new Execution Unit to the + * + * @param string $package + * @param string $version + * @param ExecutionUnit $unit + * @param bool $temporary + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws UnsupportedRunnerException + * @noinspection PhpUnused + */ + public function addUnit(string $package, string $version, ExecutionUnit $unit, bool $temporary=false): void + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Cannot add new ExecutionUnit \'' . $unit->ExecutionPolicy->Name .'\' for ' . $package . ', insufficient permissions'); + + $package_id = $this->getPackageId($package, $version); + $package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx'; + $package_bin_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id; + + $filesystem = new Filesystem(); + + // Either load or create the pointers file + if(!$filesystem->exists($package_config_path)) + { + $execution_pointers = new ExecutionPointers($package, $version); + } + else + { + $execution_pointers = ExecutionPointers::fromArray(ZiProto::decode(IO::fread($package_config_path))); + } + + $bin_file = $package_bin_path . DIRECTORY_SEPARATOR . hash('haval128,4', $unit->ExecutionPolicy->Name); + $bin_file .= match ($unit->ExecutionPolicy->Runner) { + Runners::php => Runner::getFileExtension(), + default => throw new UnsupportedRunnerException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'), + }; + + if($filesystem->exists($bin_file) && $temporary) + return; + + if(!$filesystem->exists($package_bin_path)) + $filesystem->mkdir($package_bin_path); + + if($filesystem->exists($bin_file)) + $filesystem->remove($bin_file); + + IO::fwrite($bin_file, $unit->Data); + $execution_pointers->addUnit($unit, $bin_file); + IO::fwrite($package_config_path, ZiProto::encode($execution_pointers->toArray(true))); + + if($temporary) + { + $this->TemporaryUnits[] = [ + 'package' => $package, + 'version' => $version, + 'unit' => $unit->ExecutionPolicy->Name + ]; + } + } + + /** + * Deletes and removes the installed unit + * + * @param string $package + * @param string $version + * @param string $name + * @return bool + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public function removeUnit(string $package, string $version, string $name): bool + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Cannot remove ExecutionUnit \'' . $name .'\' for ' . $package . ', insufficient permissions'); + + $package_id = $this->getPackageId($package, $version); + $package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx'; + $package_bin_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id; + + $filesystem = new Filesystem(); + if(!$filesystem->exists($package_config_path)) + return false; + $execution_pointers = ExecutionPointers::fromArray(ZiProto::decode(IO::fread($package_config_path))); + $unit = $execution_pointers->getUnit($name); + if($unit == null) + return false; + $results = $execution_pointers->deleteUnit($name); + + // Delete everything if there are no execution pointers configured + if(count($execution_pointers->getPointers()) == 0) + { + $filesystem->remove($package_config_path); + $filesystem->remove($package_bin_path); + + return $results; + } + + // Delete the single execution pointer file + if($filesystem->exists($unit->FilePointer)) + $filesystem->remove($unit->FilePointer); + + return $results; + } + + /** + * Returns an array of configured units for a package version + * + * @param string $package + * @param string $version + * @return array + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @noinspection PhpUnused + */ + public function getUnits(string $package, string $version): array + { + $package_id = $this->getPackageId($package, $version); + $package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx'; + + if(!file_exists($package_config_path)) + return []; + + $execution_pointers = ExecutionPointers::fromArray(ZiProto::decode(IO::fread($package_config_path))); + $results = []; + foreach($execution_pointers->getPointers() as $pointer) + { + $results[] = $pointer->ExecutionPolicy->Name; + } + + return $results; + } + + /** + * Executes a unit + * + * @param string $package + * @param string $version + * @param string $name + * @return void + * @throws AccessDeniedException + * @throws ExecutionUnitNotFoundException + * @throws FileNotFoundException + * @throws IOException + * @throws NoAvailableUnitsException + * @throws UnsupportedRunnerException + * @throws RunnerExecutionException + */ + public function executeUnit(string $package, string $version, string $name): void + { + $package_id = $this->getPackageId($package, $version); + $package_config_path = $this->RunnerPath . DIRECTORY_SEPARATOR . $package_id . '.inx'; + + if(!file_exists($package_config_path)) + throw new NoAvailableUnitsException('There is no available units for \'' . $package . '=' .$version .'\''); + + $execution_pointers = ExecutionPointers::fromArray(ZiProto::decode(IO::fread($package_config_path))); + $unit = $execution_pointers->getUnit($name); + + if($unit == null) + throw new ExecutionUnitNotFoundException('The execution unit \'' . $name . '\' was not found for \'' . $package . '=' .$version .'\''); + + $process = match (strtolower($unit->ExecutionPolicy->Runner)) + { + Runners::php => Runner::prepareProcess($unit), + default => throw new UnsupportedRunnerException('The runner \'' . $unit->ExecutionPolicy->Runner . '\' is not supported'), + }; + + if($unit->ExecutionPolicy->Execute->WorkingDirectory !== null) + $process->setWorkingDirectory($unit->ExecutionPolicy->Execute->WorkingDirectory); + if($unit->ExecutionPolicy->Execute->Timeout !== null) + $process->setTimeout((float)$unit->ExecutionPolicy->Execute->Timeout); + + if($unit->ExecutionPolicy->Execute->Silent) + { + $process->disableOutput(); + $process->setTty(false); + } + elseif($unit->ExecutionPolicy->Execute->Tty) + { + $process->enableOutput(); + $process->setTty(true); + } + else + { + $process->enableOutput(); + } + + try + { + if($unit->ExecutionPolicy->Message !== null) + Console::out($unit->ExecutionPolicy->Message); + + $process->run(function ($type, $buffer) { + Console::out($buffer); + }); + + $process->wait(); + } + catch(Exception $e) + { + unset($e); + $this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Error); + } + + if($unit->ExecutionPolicy->ExitHandlers !== null) + { + if($process->isSuccessful() && $unit->ExecutionPolicy->ExitHandlers->Success !== null) + { + $this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Success); + } + elseif($process->isSuccessful() && $unit->ExecutionPolicy->ExitHandlers->Error !== null) + { + $this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Error); + } + else + { + $this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Success, $process); + $this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Warning, $process); + $this->handleExit($package, $version, $unit->ExecutionPolicy->ExitHandlers->Error, $process); + } + } + } + + /** + * Temporarily executes a + * + * @param Package $package + * @param string $unit_name + * @return void + * @throws AccessDeniedException + * @throws ExecutionUnitNotFoundException + * @throws FileNotFoundException + * @throws IOException + * @throws NoAvailableUnitsException + * @throws RunnerExecutionException + * @throws UnsupportedRunnerException + */ + public function temporaryExecute(Package $package, string $unit_name): void + { + // First get the execution unit from the package. + $unit = $package->getExecutionUnit($unit_name); + + // Get the required units + $required_units = []; + if($unit->ExecutionPolicy->ExitHandlers !== null) + { + $required_unit = $unit->ExecutionPolicy?->ExitHandlers?->Success?->Run; + if($required_unit !== null) + $required_units[] = $required_unit; + + $required_unit = $unit->ExecutionPolicy?->ExitHandlers?->Warning?->Run; + if($required_unit !== null) + $required_units[] = $required_unit; + + $required_unit = $unit->ExecutionPolicy?->ExitHandlers?->Error?->Run; + if($required_unit !== null) + $required_units = $required_unit; + } + + // Install the units temporarily + $this->addUnit($package->Assembly->Package, $package->Assembly->Version, $unit, true); + foreach($required_units as $r_unit) + { + $this->addUnit($package->Assembly->Package, $package->Assembly->Version, $r_unit, true); + } + + $this->executeUnit($package->Assembly->Package, $package->Assembly->Version, $unit_name); + $this->cleanTemporaryUnits(); + } + + /** + * Handles an exit handler object. + * + * If Process is Null and EndProcess is true, the method will end the process + * if Process is not Null the exit handler will only execute if the process' exit code is the same + * + * @param string $package + * @param string $version + * @param ExitHandle $exitHandle + * @param Process|null $process + * @return bool + * @throws AccessDeniedException + * @throws ExecutionUnitNotFoundException + * @throws FileNotFoundException + * @throws IOException + * @throws NoAvailableUnitsException + * @throws RunnerExecutionException + * @throws UnsupportedRunnerException + */ + public function handleExit(string $package, string $version, ExitHandle $exitHandle, ?Process $process=null): bool + { + if($exitHandle->Message !== null) + Console::out($exitHandle->Message); + + if($process !== null && !$exitHandle->EndProcess) + { + if($exitHandle->ExitCode !== $process->getExitCode()) + return false; + } + elseif($exitHandle->EndProcess) + { + exit($exitHandle->ExitCode); + } + + if($exitHandle->Run !== null) + { + $this->executeUnit($package, $version, $exitHandle->Run); + } + + return true; + } + + } \ No newline at end of file diff --git a/src/ncc/Managers/PackageLockManager.php b/src/ncc/Managers/PackageLockManager.php new file mode 100644 index 0000000..4c7d6cb --- /dev/null +++ b/src/ncc/Managers/PackageLockManager.php @@ -0,0 +1,152 @@ +PackageLockPath = PathFinder::getPackageLock(Scopes::System); + + try + { + $this->load(); + } + catch (PackageLockException $e) + { + unset($e); + } + } + + /** + * Loads the PackageLock from the disk + * + * @return void + * @throws PackageLockException + */ + public function load(): void + { + if(RuntimeCache::get($this->PackageLockPath) !== null) + { + $this->PackageLock = RuntimeCache::get($this->PackageLockPath); + return; + } + + if(file_exists($this->PackageLockPath) && is_file($this->PackageLockPath)) + { + try + { + Console::outDebug('reading package lock file'); + $data = IO::fread($this->PackageLockPath); + if(strlen($data) > 0) + { + $this->PackageLock = PackageLock::fromArray(ZiProto::decode($data)); + } + else + { + $this->PackageLock = new PackageLock(); + } + } + catch(Exception $e) + { + throw new PackageLockException('The PackageLock file cannot be parsed', $e); + } + } + else + { + $this->PackageLock = new PackageLock(); + } + + RuntimeCache::set($this->PackageLockPath, $this->PackageLock); + } + + /** + * Saves the PackageLock to disk + * + * @return void + * @throws AccessDeniedException + * @throws PackageLockException + */ + public function save(): void + { + // Don't save something that isn't loaded lol + if($this->PackageLock == null) + return; + + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Cannot write to PackageLock, insufficient permissions'); + + try + { + IO::fwrite($this->PackageLockPath, ZiProto::encode($this->PackageLock->toArray(true)), 0755); + RuntimeCache::set($this->PackageLockPath, $this->PackageLock); + } + catch(IOException $e) + { + throw new PackageLockException('Cannot save the package lock file to disk', $e); + } + } + + /** + * Constructs the package lock file if it doesn't exist + * + * @return void + * @throws AccessDeniedException + * @throws PackageLockException + */ + public function constructLockFile(): void + { + try + { + $this->load(); + } + catch (PackageLockException $e) + { + unset($e); + $this->PackageLock = new PackageLock(); + } + + $this->save(); + } + + /** + * @return PackageLock|null + * @throws PackageLockException + */ + public function getPackageLock(): ?PackageLock + { + if($this->PackageLock == null) + $this->load(); + return $this->PackageLock; + } + } \ No newline at end of file diff --git a/src/ncc/Managers/PackageManager.php b/src/ncc/Managers/PackageManager.php new file mode 100644 index 0000000..3719e2b --- /dev/null +++ b/src/ncc/Managers/PackageManager.php @@ -0,0 +1,490 @@ +PackagesPath = PathFinder::getPackagesPath(Scopes::System); + $this->PackageLockManager = new PackageLockManager(); + $this->PackageLockManager->load(); + } + + /** + * Installs a local package onto the system + * + * @param string $input + * @return string + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws InstallationException + * @throws PackageAlreadyInstalledException + * @throws PackageLockException + * @throws PackageParsingException + * @throws UnsupportedCompilerExtensionException + * @throws UnsupportedRunnerException + * @throws VersionNotFoundException + */ + public function install(string $input): string + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Insufficient permission to install packages'); + + Console::outVerbose(sprintf('Installing %s', $input)); + if(!file_exists($input) || !is_file($input) || !is_readable($input)) + throw new FileNotFoundException('The specified file \'' . $input .' \' does not exist or is not readable.'); + + $package = Package::load($input); + + if($this->getPackageVersion($package->Assembly->Package, $package->Assembly->Version) !== null) + throw new PackageAlreadyInstalledException('The package ' . $package->Assembly->Package . '==' . $package->Assembly->Version . ' is already installed'); + + $extension = $package->Header->CompilerExtension->Extension; + $installation_paths = new InstallationPaths($this->PackagesPath . DIRECTORY_SEPARATOR . $package->Assembly->Package . '==' . $package->Assembly->Version); + $installer = match ($extension) { + CompilerExtensions::PHP => new Installer($package), + default => throw new UnsupportedCompilerExtensionException('The compiler extension \'' . $extension . '\' is not supported'), + }; + $execution_pointer_manager = new ExecutionPointerManager(); + PackageCompiler::compilePackageConstants($package, [ + ConstantReferences::Install => $installation_paths + ]); + + Console::outVerbose(sprintf('Successfully parsed %s', $package->Assembly->Package)); + + if(Resolver::checkLogLevel(LogLevel::Debug, Main::getLogLevel())) + { + Console::outDebug(sprintf('installer.install_path: %s', $installation_paths->getInstallationPath())); + Console::outDebug(sprintf('installer.data_path: %s', $installation_paths->getDataPath())); + Console::outDebug(sprintf('installer.bin_path: %s', $installation_paths->getBinPath())); + Console::outDebug(sprintf('installer.src_path: %s', $installation_paths->getSourcePath())); + + foreach($package->Assembly->toArray() as $prop => $value) + Console::outDebug(sprintf('assembly.%s: %s', $prop, ($value ?? 'n/a'))); + foreach($package->Header->CompilerExtension->toArray() as $prop => $value) + Console::outDebug(sprintf('header.compiler.%s: %s', $prop, ($value ?? 'n/a'))); + } + + Console::out('Installing ' . $package->Assembly->Package); + + // 4 For Directory Creation, preInstall, postInstall & initData methods + $steps = (4 + count($package->Components) + count ($package->Resources) + count ($package->ExecutionUnits)); + + // Include the Execution units + if($package->Installer?->PreInstall !== null) + $steps += count($package->Installer->PreInstall); + if($package->Installer?->PostInstall!== null) + $steps += count($package->Installer->PostInstall); + + $current_steps = 0; + $filesystem = new Filesystem(); + + try + { + $filesystem->mkdir($installation_paths->getInstallationPath(), 0755); + $filesystem->mkdir($installation_paths->getBinPath(), 0755); + $filesystem->mkdir($installation_paths->getDataPath(), 0755); + $filesystem->mkdir($installation_paths->getSourcePath(), 0755); + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + catch(Exception $e) + { + throw new InstallationException('Error while creating directory, ' . $e->getMessage(), $e); + } + + try + { + self::initData($package, $installation_paths); + Console::outDebug(sprintf('saving shadow package to %s', $installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'pkg')); + $package->save($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'pkg'); + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + catch(Exception $e) + { + throw new InstallationException('Cannot initialize package install, ' . $e->getMessage(), $e); + } + + // Execute the pre-installation stage before the installation stage + try + { + $installer->preInstall($installation_paths); + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + catch (Exception $e) + { + throw new InstallationException('Pre installation stage failed, ' . $e->getMessage(), $e); + } + + if($package->Installer?->PreInstall !== null && count($package->Installer->PreInstall) > 0) + { + foreach($package->Installer->PreInstall as $unit_name) + { + try + { + $execution_pointer_manager->temporaryExecute($package, $unit_name); + } + catch(Exception $e) + { + Console::outWarning('Cannot execute unit ' . $unit_name . ', ' . $e->getMessage()); + } + + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + } + + // Process & Install the components + foreach($package->Components as $component) + { + Console::outDebug(sprintf('processing component %s (%s)', $component->Name, $component->DataType)); + + try + { + $data = $installer->processComponent($component); + if($data !== null) + { + $component_path = $installation_paths->getSourcePath() . DIRECTORY_SEPARATOR . $component->Name; + $component_dir = dirname($component_path); + if(!$filesystem->exists($component_dir)) + $filesystem->mkdir($component_dir); + IO::fwrite($component_path, $data); + } + } + catch(Exception $e) + { + throw new InstallationException('Cannot process one or more components, ' . $e->getMessage(), $e); + } + + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + + // Process & Install the resources + foreach($package->Resources as $resource) + { + Console::outDebug(sprintf('processing resource %s', $resource->Name)); + + try + { + $data = $installer->processResource($resource); + if($data !== null) + { + $resource_path = $installation_paths->getSourcePath() . DIRECTORY_SEPARATOR . $resource->Name; + $resource_dir = dirname($resource_path); + if(!$filesystem->exists($resource_dir)) + $filesystem->mkdir($resource_dir); + IO::fwrite($resource_path, $data); + } + } + catch(Exception $e) + { + throw new InstallationException('Cannot process one or more resources, ' . $e->getMessage(), $e); + } + + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + + // Install execution units + // TODO: Implement symlink support + if(count($package->ExecutionUnits) > 0) + { + $execution_pointer_manager = new ExecutionPointerManager(); + $unit_paths = []; + + foreach($package->ExecutionUnits as $executionUnit) + { + $execution_pointer_manager->addUnit($package->Assembly->Package, $package->Assembly->Version, $executionUnit); + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + + IO::fwrite($installation_paths->getDataPath() . DIRECTORY_SEPARATOR . 'exec', ZiProto::encode($unit_paths)); + } + + // Execute the post-installation stage after the installation is complete + try + { + $installer->postInstall($installation_paths); + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + catch (Exception $e) + { + throw new InstallationException('Post installation stage failed, ' . $e->getMessage(), $e); + } + + if($package->Installer?->PostInstall !== null && count($package->Installer->PostInstall) > 0) + { + foreach($package->Installer->PostInstall as $unit_name) + { + try + { + $execution_pointer_manager->temporaryExecute($package, $unit_name); + } + catch(Exception $e) + { + Console::outWarning('Cannot execute unit ' . $unit_name . ', ' . $e->getMessage()); + } + + $current_steps += 1; + Console::inlineProgressBar($current_steps, $steps); + } + } + + $this->getPackageLockManager()->getPackageLock()->addPackage($package, $installation_paths->getInstallationPath()); + $this->getPackageLockManager()->save(); + + return $package->Assembly->Package; + } + + /** + * Returns an existing package entry, returns null if no such entry exists + * + * @param string $package + * @return PackageEntry|null + * @throws PackageLockException + * @throws PackageLockException + */ + public function getPackage(string $package): ?PackageEntry + { + return $this->getPackageLockManager()->getPackageLock()->getPackage($package); + } + + /** + * Returns an existing version entry, returns null if no such entry exists + * + * @param string $package + * @param string $version + * @return VersionEntry|null + * @throws VersionNotFoundException + * @throws PackageLockException + */ + public function getPackageVersion(string $package, string $version): ?VersionEntry + { + return $this->getPackage($package)?->getVersion($version); + } + + /** + * Returns the latest version of the package, or null if there is no entry + * + * @param string $package + * @return VersionEntry|null + * @throws VersionNotFoundException + * @throws PackageLockException + */ + public function getLatestVersion(string $package): ?VersionEntry + { + return $this->getPackage($package)?->getVersion($this->getPackage($package)?->getLatestVersion()); + } + + /** + * Returns an array of all packages and their installed versions + * + * @return array + * @throws PackageLockException + * @throws PackageLockException + */ + public function getInstalledPackages(): array + { + return $this->getPackageLockManager()->getPackageLock()->getPackages(); + } + + /** + * Uninstalls a package version + * + * @param string $package + * @param string $version + * @return void + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws PackageLockException + * @throws PackageNotFoundException + * @throws VersionNotFoundException + */ + public function uninstallPackageVersion(string $package, string $version): void + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Insufficient permission to uninstall packages'); + + $version_entry = $this->getPackageVersion($package, $version); + if($version_entry == null) + throw new PackageNotFoundException(sprintf('The package %s==%s was not found', $package, $version)); + + Console::out(sprintf('Uninstalling %s==%s', $package, $version)); + Console::outVerbose(sprintf('Removing package %s==%s from PackageLock', $package, $version)); + if(!$this->getPackageLockManager()->getPackageLock()->removePackageVersion($package, $version)) + Console::outDebug('warning: removing package from package lock failed'); + + $this->getPackageLockManager()->save(); + + Console::outVerbose('Removing package files'); + $scanner = new DirectoryScanner(); + $filesystem = new Filesystem(); + + /** @var SplFileInfo $item */ + /** @noinspection PhpRedundantOptionalArgumentInspection */ + foreach($scanner($version_entry->Location, true) as $item) + { + if(is_file($item->getPath())) + { + Console::outDebug(sprintf('deleting %s', $item->getPath())); + $filesystem->remove($item->getPath()); + } + } + + $filesystem->remove($version_entry->Location); + + if($version_entry->ExecutionUnits !== null && count($version_entry->ExecutionUnits) > 0) + { + Console::outVerbose('Uninstalling execution units'); + + $execution_pointer_manager = new ExecutionPointerManager(); + foreach($version_entry->ExecutionUnits as $executionUnit) + { + if(!$execution_pointer_manager->removeUnit($package, $version, $executionUnit->ExecutionPolicy->Name)) + Console::outDebug(sprintf('warning: removing execution unit %s failed', $executionUnit->ExecutionPolicy->Name)); + } + } + } + + /** + * Uninstalls all versions of a package + * + * @param string $package + * @return void + * @throws AccessDeniedException + * @throws PackageLockException + * @throws PackageNotFoundException + * @throws VersionNotFoundException + */ + public function uninstallPackage(string $package): void + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Insufficient permission to uninstall packages'); + + $package_entry = $this->getPackage($package); + if($package_entry == null) + throw new PackageNotFoundException(sprintf('The package %s was not found', $package)); + + foreach($package_entry->getVersions() as $version) + { + $version_entry = $package_entry->getVersion($version); + try + { + $this->uninstallPackageVersion($package, $version_entry->Version); + } + catch(Exception $e) + { + Console::outDebug(sprintf('warning: unable to uninstall package %s==%s, %s (%s)', $package, $version_entry->Version, $e->getMessage(), $e->getCode())); + } + } + } + + /** + * @param Package $package + * @param InstallationPaths $paths + * @throws InstallationException + */ + private static function initData(Package $package, InstallationPaths $paths): void + { + // Create data files + $dependencies = []; + foreach($package->Dependencies as $dependency) + { + $dependencies[] = $dependency->toArray(true); + } + + $data_files = [ + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'assembly' => + ZiProto::encode($package->Assembly->toArray(true)), + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'ext' => + ZiProto::encode($package->Header->CompilerExtension->toArray(true)), + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'const' => + ZiProto::encode($package->Header->RuntimeConstants), + $paths->getDataPath() . DIRECTORY_SEPARATOR . 'dependencies' => + ZiProto::encode($dependencies), + ]; + + foreach($data_files as $file => $data) + { + try + { + IO::fwrite($file, $data); + } + catch (IOException $e) + { + throw new InstallationException('Cannot write to file \'' . $file . '\', ' . $e->getMessage(), $e); + } + } + } + + /** + * @return PackageLockManager|null + */ + private function getPackageLockManager(): ?PackageLockManager + { + if($this->PackageLockManager == null) + { + $this->PackageLockManager = new PackageLockManager(); + } + + return $this->PackageLockManager; + } + + } \ No newline at end of file diff --git a/src/ncc/Managers/ProjectManager.php b/src/ncc/Managers/ProjectManager.php index a9d0e42..286910b 100644 --- a/src/ncc/Managers/ProjectManager.php +++ b/src/ncc/Managers/ProjectManager.php @@ -1,12 +1,26 @@ SelectedDirectory = $selected_directory; $this->ProjectFilePath = null; $this->ProjectPath = null; - $this->detectProjectPath(); - } - - /** - * Attempts to resolve the project path from the selected directory - * Returns false if the selected directory is not a proper project or an initialized project - * - * @return void - */ - private function detectProjectPath(): void - { - $selected_directory = $this->SelectedDirectory; - // Auto-resolve the trailing slash /** @noinspection PhpStrFunctionsInspection */ - if(substr($selected_directory, -1) !== '/') + if(substr($path, -1) !== '/') { - $selected_directory .= DIRECTORY_SEPARATOR; + $path .= DIRECTORY_SEPARATOR; } // Detect if the folder exists or not - if(!file_exists($selected_directory) || !is_dir($selected_directory)) + if(!file_exists($path) || !is_dir($path)) { - return; + throw new DirectoryNotFoundException('The given directory \'' . $path .'\' does not exist'); } - $this->ProjectPath = $selected_directory; - $this->ProjectFilePath = $selected_directory . 'project.json'; + $this->ProjectPath = $path; + $this->ProjectFilePath = $path . 'project.json'; + + if(file_exists($this->ProjectFilePath)) + $this->load(); } /** * Initializes the project structure * - * // TODO: Correct the unexpected path behavior issue when initializing a project - * * @param Compiler $compiler * @param string $name * @param string $package - * @param string $src + * @param string|null $src * @param array $options * @throws InvalidPackageNameException * @throws InvalidProjectNameException * @throws MalformedJsonException * @throws ProjectAlreadyExistsException */ - public function initializeProject(Compiler $compiler, string $name, string $package, string $src, array $options=[]): void + public function initializeProject(Compiler $compiler, string $name, string $package, ?string $src=null, array $options=[]): void { // Validate the project information first if(!Validate::packageName($package)) @@ -109,41 +116,43 @@ throw new ProjectAlreadyExistsException('A project has already been initialized in \'' . $this->ProjectPath . DIRECTORY_SEPARATOR . 'project.json' . '\''); } - $Project = new ProjectConfiguration(); + $this->ProjectConfiguration = new ProjectConfiguration(); // Set the compiler information - $Project->Project->Compiler = $compiler; + $this->ProjectConfiguration->Project->Compiler = $compiler; // Set the assembly information - $Project->Assembly->Name = $name; - $Project->Assembly->Package = $package; - $Project->Assembly->Version = '1.0.0'; - $Project->Assembly->UUID = Uuid::v1()->toRfc4122(); + $this->ProjectConfiguration->Assembly->Name = $name; + $this->ProjectConfiguration->Assembly->Package = $package; + $this->ProjectConfiguration->Assembly->Version = '1.0.0'; + $this->ProjectConfiguration->Assembly->UUID = Uuid::v1()->toRfc4122(); // Set the build information - $Project->Build->SourcePath = $src; - $Project->Build->DefaultConfiguration = 'debug'; + $this->ProjectConfiguration->Build->SourcePath = $src; + if($this->ProjectConfiguration->Build->SourcePath == null) + $this->ProjectConfiguration->Build->SourcePath = $this->ProjectPath; + $this->ProjectConfiguration->Build->DefaultConfiguration = 'debug'; // Assembly constants if the program wishes to check for this - $Project->Build->DefineConstants['ASSEMBLY_NAME'] = '%ASSEMBLY.NAME%'; - $Project->Build->DefineConstants['ASSEMBLY_PACKAGE'] = '%ASSEMBLY.PACKAGE%'; - $Project->Build->DefineConstants['ASSEMBLY_VERSION'] = '%ASSEMBLY.VERSION%'; - $Project->Build->DefineConstants['ASSEMBLY_UID'] = '%ASSEMBLY.UID%'; + $this->ProjectConfiguration->Build->DefineConstants['ASSEMBLY_NAME'] = '%ASSEMBLY.NAME%'; + $this->ProjectConfiguration->Build->DefineConstants['ASSEMBLY_PACKAGE'] = '%ASSEMBLY.PACKAGE%'; + $this->ProjectConfiguration->Build->DefineConstants['ASSEMBLY_VERSION'] = '%ASSEMBLY.VERSION%'; + $this->ProjectConfiguration->Build->DefineConstants['ASSEMBLY_UID'] = '%ASSEMBLY.UID%'; // Generate configurations $DebugConfiguration = new ProjectConfiguration\BuildConfiguration(); $DebugConfiguration->Name = 'debug'; $DebugConfiguration->OutputPath = 'build/debug'; $DebugConfiguration->DefineConstants["DEBUG"] = '1'; // Debugging constant if the program wishes to check for this - $Project->Build->Configurations[] = $DebugConfiguration; + $this->ProjectConfiguration->Build->Configurations[] = $DebugConfiguration; $ReleaseConfiguration = new ProjectConfiguration\BuildConfiguration(); $ReleaseConfiguration->Name = 'release'; $ReleaseConfiguration->OutputPath = 'build/release'; $ReleaseConfiguration->DefineConstants["DEBUG"] = '0'; // Debugging constant if the program wishes to check for this - $Project->Build->Configurations[] = $ReleaseConfiguration; + $this->ProjectConfiguration->Build->Configurations[] = $ReleaseConfiguration; // Finally create project.json - $Project->toFile($this->ProjectPath . DIRECTORY_SEPARATOR . 'project.json'); + $this->ProjectConfiguration->toFile($this->ProjectPath . DIRECTORY_SEPARATOR . 'project.json'); // And create the project directory for additional assets/resources $Folders = [ @@ -166,15 +175,59 @@ switch($option) { case InitializeProjectOptions::CREATE_SOURCE_DIRECTORY: - if(!file_exists($this->ProjectPath . DIRECTORY_SEPARATOR . 'src')) + if(!file_exists($this->ProjectConfiguration->Build->SourcePath)) { - mkdir($this->ProjectPath . DIRECTORY_SEPARATOR . 'src'); + mkdir($this->ProjectConfiguration->Build->SourcePath); } break; } } } + /** + * Determines if a project configuration is loaded or not + * + * @return bool + */ + public function projectLoaded(): bool + { + if($this->ProjectConfiguration == null) + return false; + + return true; + } + + /** + * Attempts to load the project configuration + * + * @return void + * @throws MalformedJsonException + * @throws ProjectConfigurationNotFoundException + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public function load() + { + if(!file_exists($this->ProjectFilePath) && !is_file($this->ProjectFilePath)) + throw new ProjectConfigurationNotFoundException('The project configuration file \'' . $this->ProjectFilePath . '\' was not found'); + + $this->ProjectConfiguration = ProjectConfiguration::fromFile($this->ProjectFilePath); + } + + /** + * Saves the project configuration + * + * @return void + * @throws MalformedJsonException + */ + public function save() + { + if(!$this->projectLoaded()) + return; + $this->ProjectConfiguration->toFile($this->ProjectFilePath); + } + /** * @return string|null */ @@ -182,4 +235,49 @@ { return $this->ProjectFilePath; } + + /** + * @return ProjectConfiguration|null + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws MalformedJsonException + * @throws ProjectConfigurationNotFoundException + */ + public function getProjectConfiguration(): ?ProjectConfiguration + { + if($this->ProjectConfiguration == null) + $this->load(); + return $this->ProjectConfiguration; + } + + /** + * @return string|null + */ + public function getProjectPath(): ?string + { + return $this->ProjectPath; + } + + + /** + * Compiles the project into a package + * + * @param string $build_configuration + * @return string + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + * @throws MalformedJsonException + * @throws ProjectConfigurationNotFoundException + * @throws BuildConfigurationNotFoundException + * @throws BuildException + * @throws PackagePreparationFailedException + * @throws UnsupportedCompilerExtensionException + * @throws UnsupportedRunnerException + */ + public function build(string $build_configuration=BuildConfigurationValues::DefaultConfiguration): string + { + return PackageCompiler::compile($this, $build_configuration); + } } \ No newline at end of file diff --git a/src/ncc/Objects/ExecutionPointers.php b/src/ncc/Objects/ExecutionPointers.php new file mode 100644 index 0000000..9181c03 --- /dev/null +++ b/src/ncc/Objects/ExecutionPointers.php @@ -0,0 +1,171 @@ +Package = $package; + $this->Version = $version; + $this->Pointers = []; + } + + /** + * Adds an Execution Unit as a pointer + * + * @param ExecutionUnit $unit + * @param bool $overwrite + * @return bool + */ + public function addUnit(ExecutionUnit $unit, string $bin_file, bool $overwrite=true): bool + { + if(Validate::exceedsPathLength($bin_file)) + return false; + + if(!file_exists($bin_file)) + throw new FileNotFoundException('The file ' . $unit->Data . ' does not exist, cannot add unit \'' . $unit->ExecutionPolicy->Name . '\''); + + if($overwrite) + { + $this->deleteUnit($unit->ExecutionPolicy->Name); + } + elseif($this->getUnit($unit->ExecutionPolicy->Name) !== null) + { + return false; + } + + $this->Pointers[] = new ExecutionPointer($unit, $bin_file); + return true; + } + + /** + * Deletes an existing unit from execution pointers + * + * @param string $name + * @return bool + */ + public function deleteUnit(string $name): bool + { + $unit = $this->getUnit($name); + if($unit == null) + return false; + + $new_pointers = []; + foreach($this->Pointers as $pointer) + { + if($pointer->ExecutionPolicy->Name !== $name) + $new_pointers[] = $pointer; + } + + $this->Pointers = $new_pointers; + return true; + } + + /** + * Returns an existing unit from the pointers + * + * @param string $name + * @return ExecutionPointer|null + */ + public function getUnit(string $name): ?ExecutionPointer + { + foreach($this->Pointers as $pointer) + { + if($pointer->ExecutionPolicy->Name == $name) + return $pointer; + } + + return null; + } + + /** + * Returns an array of execution pointers that are currently configured + * + * @return array|ExecutionPointer[] + */ + public function getPointers(): array + { + return $this->Pointers; + } + + /** + * Returns the version of the package that uses these execution policies. + * + * @return string + */ + public function getVersion(): string + { + return $this->Version; + } + + /** + * Returns the name of the package that uses these execution policies + * + * @return string + */ + public function getPackage(): string + { + return $this->Package; + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + $pointers = []; + foreach($this->Pointers as $pointer) + { + $pointers[] = $pointer->toArray($bytecode); + } + return $pointers; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return ExecutionPointers + */ + public static function fromArray(array $data): self + { + $object = new self(); + + foreach($data as $datum) + { + $object->Pointers[] = ExecutionPointer::fromArray($datum); + } + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php b/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php new file mode 100644 index 0000000..9a5747e --- /dev/null +++ b/src/ncc/Objects/ExecutionPointers/ExecutionPointer.php @@ -0,0 +1,78 @@ +ID = $unit->getID(); + $this->ExecutionPolicy = $unit->ExecutionPolicy; + $this->FilePointer = $bin_file; + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + return [ + ($bytecode ? Functions::cbc('id') : 'id') => $this->ID, + ($bytecode ? Functions::cbc('execution_policy') : 'execution_policy') => $this->ExecutionPolicy->toArray($bytecode), + ($bytecode ? Functions::cbc('file_pointer') : 'file_pointer') => $this->FilePointer, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return ExecutionPointer + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->ID = Functions::array_bc($data, 'id'); + $object->ExecutionPolicy = Functions::array_bc($data, 'execution_policy'); + $object->FilePointer = Functions::array_bc($data, 'file_pointer'); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/InstallationPaths.php b/src/ncc/Objects/InstallationPaths.php new file mode 100644 index 0000000..d7c745a --- /dev/null +++ b/src/ncc/Objects/InstallationPaths.php @@ -0,0 +1,61 @@ +InstallationPath = $installation_path; + } + + /** + * Returns the data path where NCC's metadata & runtime information is stored + * + * @return string + */ + public function getDataPath(): string + { + return $this->InstallationPath . DIRECTORY_SEPARATOR . 'ncc'; + } + + /** + * Returns the source path for where the package resides + * + * @return string + */ + public function getSourcePath(): string + { + return $this->InstallationPath . DIRECTORY_SEPARATOR . 'src'; + } + + /** + * Returns the path for where executables are located + * + * @return string + */ + public function getBinPath(): string + { + return $this->InstallationPath . DIRECTORY_SEPARATOR . 'bin'; + } + + /** + * @return string + */ + public function getInstallationPath(): string + { + return $this->InstallationPath; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/NccVersionInformation/Component.php b/src/ncc/Objects/NccVersionInformation/Component.php index 68b7568..33292ad 100644 --- a/src/ncc/Objects/NccVersionInformation/Component.php +++ b/src/ncc/Objects/NccVersionInformation/Component.php @@ -1,8 +1,14 @@ Vendor . DIRECTORY_SEPARATOR . $this->PackageName . DIRECTORY_SEPARATOR; - if(file_exists($component_path . 'VERSION') == false) - { + if(!file_exists($component_path . 'VERSION')) throw new ComponentVersionNotFoundException('The file \'' . $component_path . 'VERSION' . '\' does not exist'); - } - return file_get_contents($component_path . 'VERSION'); + return IO::fread($component_path . 'VERSION'); } /** diff --git a/src/ncc/Objects/Package.php b/src/ncc/Objects/Package.php index 78bf4dc..4dad3a6 100644 --- a/src/ncc/Objects/Package.php +++ b/src/ncc/Objects/Package.php @@ -4,17 +4,24 @@ namespace ncc\Objects; + use Exception; + use ncc\Abstracts\EncoderType; + use ncc\Abstracts\PackageStructureVersions; + use ncc\Exceptions\FileNotFoundException; use ncc\Exceptions\InvalidPackageException; use ncc\Exceptions\InvalidProjectConfigurationException; + use ncc\Exceptions\IOException; + use ncc\Exceptions\PackageParsingException; use ncc\Objects\Package\Component; + use ncc\Objects\Package\ExecutionUnit; use ncc\Objects\Package\Header; use ncc\Objects\Package\Installer; use ncc\Objects\Package\MagicBytes; - use ncc\Objects\Package\MainExecutionPolicy; use ncc\Objects\Package\Resource; use ncc\Objects\ProjectConfiguration\Assembly; use ncc\Objects\ProjectConfiguration\Dependency; use ncc\Utilities\Functions; + use ncc\Utilities\IO; use ncc\ZiProto\ZiProto; class Package @@ -50,7 +57,7 @@ /** * The Main Execution Policy object for the package if the package is an executable package. * - * @var MainExecutionPolicy|null + * @var string|null */ public $MainExecutionPolicy; @@ -61,6 +68,13 @@ */ public $Installer; + /** + * An array of execution units defined in the package + * + * @var ExecutionUnit[] + */ + public $ExecutionUnits; + /** * An array of resources that the package depends on * @@ -83,6 +97,7 @@ $this->MagicBytes = new MagicBytes(); $this->Header = new Header(); $this->Assembly = new Assembly(); + $this->ExecutionUnits = []; $this->Components = []; $this->Dependencies = []; $this->Resources = []; @@ -126,16 +141,141 @@ return true; } + /** + * Attempts to find the execution unit with the given name + * + * @param string $name + * @return ExecutionUnit|null + */ + public function getExecutionUnit(string $name): ?ExecutionUnit + { + foreach($this->ExecutionUnits as $unit) + { + if($unit->ExecutionPolicy->Name == $name) + return $unit; + } + + return null; + } + /** * Writes the package contents to disk * * @param string $output_path * @return void + * @throws IOException */ public function save(string $output_path): void { $package_contents = $this->MagicBytes->toString() . ZiProto::encode($this->toArray(true)); - file_put_contents($output_path, $package_contents); + IO::fwrite($output_path, $package_contents, 0777); + } + + /** + * Attempts to parse the specified package path and returns the object representation + * of the package, including with the MagicBytes representation that is in the + * file headers. + * + * @param string $path + * @return Package + * @throws FileNotFoundException + * @throws PackageParsingException + */ + public static function load(string $path): Package + { + if(!file_exists($path) || !is_file($path) || !is_readable($path)) + { + throw new FileNotFoundException('The file ' . $path . ' does not exist or is not readable'); + } + + $handle = fopen($path, "rb"); + $header = fread($handle, 256); // Read the first 256 bytes of the file + fclose($handle); + + if(!strtoupper(substr($header, 0, 11)) == 'NCC_PACKAGE') + throw new PackageParsingException('The package \'' . $path . '\' does not appear to be a valid NCC Package (Missing Header)'); + + // Extract the package structure version + $package_structure_version = strtoupper(substr($header, 11, 3)); + + if(!in_array($package_structure_version, PackageStructureVersions::ALL)) + throw new PackageParsingException('The package \'' . $path . '\' has a package structure version of ' . $package_structure_version . ' which is not supported by this version NCC'); + + // Extract the package encoding type and package type + $encoding_header = strtoupper(substr($header, 14, 5)); + $encoding_type = substr($encoding_header, 0, 3); + $package_type = substr($encoding_header, 3, 2); + + $magic_bytes = new MagicBytes(); + $magic_bytes->PackageStructureVersion = $package_structure_version; + + // Determine the encoding type + switch($encoding_type) + { + case '300': + $magic_bytes->Encoder = EncoderType::ZiProto; + $magic_bytes->IsCompressed = false; + $magic_bytes->IsEncrypted = false; + break; + + case '301': + $magic_bytes->Encoder = EncoderType::ZiProto; + $magic_bytes->IsCompressed = true; + $magic_bytes->IsEncrypted = false; + break; + + case '310': + $magic_bytes->Encoder = EncoderType::ZiProto; + $magic_bytes->IsCompressed = false; + $magic_bytes->IsEncrypted = true; + break; + + case '311': + $magic_bytes->Encoder = EncoderType::ZiProto; + $magic_bytes->IsCompressed = true; + $magic_bytes->IsEncrypted = true; + break; + + default: + throw new PackageParsingException('Cannot determine the encoding type for the package \'' . $path . '\' (Got ' . $encoding_type . ')'); + } + + // Determine the package type + switch($package_type) + { + case '40': + $magic_bytes->IsInstallable = true; + $magic_bytes->IsExecutable = false; + break; + + case '41': + $magic_bytes->IsInstallable = false; + $magic_bytes->IsExecutable = true; + break; + + case '42': + $magic_bytes->IsInstallable = true; + $magic_bytes->IsExecutable = true; + break; + + default: + throw new PackageParsingException('Cannot determine the package type for the package \'' . $path . '\' (Got ' . $package_type . ')'); + } + + // TODO: Implement encryption and compression parsing + + // Assuming all is good, load the entire fire into memory and parse its contents + try + { + $package = Package::fromArray(ZiProto::decode(substr(IO::fread($path), strlen($magic_bytes->toString())))); + } + catch(Exception $e) + { + throw new PackageParsingException('Cannot decode the contents of the package \'' . $path . '\', invalid encoding or the package is corrupted, ' . $e->getMessage(), $e); + } + + $package->MagicBytes = $magic_bytes; + return $package; } /** @@ -161,13 +301,17 @@ foreach($this->Resources as $resource) $_resources[] = $resource->toArray($bytecode); + $_execution_units = []; + foreach($this->ExecutionUnits as $unit) + $_execution_units[] = $unit->toArray($bytecode); return [ - ($bytecode ? Functions::cbc('header') : 'header') => $this->Header?->toArray($bytecode), - ($bytecode ? Functions::cbc('assembly') : 'assembly') => $this->Assembly?->toArray($bytecode), + ($bytecode ? Functions::cbc('header') : 'header') => $this?->Header?->toArray($bytecode), + ($bytecode ? Functions::cbc('assembly') : 'assembly') => $this?->Assembly?->toArray($bytecode), ($bytecode ? Functions::cbc('dependencies') : 'dependencies') => $_dependencies, - ($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this->MainExecutionPolicy?->toArray($bytecode), - ($bytecode ? Functions::cbc('installer') : 'installer') => $this->Installer?->toArray($bytecode), + ($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this?->MainExecutionPolicy, + ($bytecode ? Functions::cbc('installer') : 'installer') => $this?->Installer?->toArray($bytecode), + ($bytecode ? Functions::cbc('execution_units') : 'execution_units') => $_execution_units, ($bytecode ? Functions::cbc('resources') : 'resources') => $_resources, ($bytecode ? Functions::cbc('components') : 'components') => $_components ]; @@ -190,8 +334,6 @@ $object->Assembly = Assembly::fromArray($object->Assembly); $object->MainExecutionPolicy = Functions::array_bc($data, 'main_execution_policy'); - if($object->MainExecutionPolicy !== null) - $object->MainExecutionPolicy = MainExecutionPolicy::fromArray($object->MainExecutionPolicy); $object->Installer = Functions::array_bc($data, 'installer'); if($object->Installer !== null) @@ -224,6 +366,15 @@ } } + $_execution_units = Functions::array_bc($data, 'execution_units'); + if($_execution_units !== null) + { + foreach($_execution_units as $unit) + { + $object->ExecutionUnits[] = ExecutionUnit::fromArray($unit); + } + } + return $object; } } \ No newline at end of file diff --git a/src/ncc/Objects/Package/Component.php b/src/ncc/Objects/Package/Component.php index 75406f7..b48708b 100644 --- a/src/ncc/Objects/Package/Component.php +++ b/src/ncc/Objects/Package/Component.php @@ -1,5 +1,7 @@ Checksum === null) - return false; + return true; // Return true if the checksum is empty if($this->Data === null) - return false; + return true; // Return true if the data is null - if(hash('sha1', $this->Data) !== $this->Checksum) - return false; + if(hash('sha1', $this->Data, true) !== $this->Checksum) + return false; // Return false if the checksum failed return true; } + /** + * Updates the checksum of the resource + * + * @return void + */ + public function updateChecksum(): void + { + $this->Checksum = null; + + if(gettype($this->Data) == 'string') + { + $this->Checksum = hash('sha1', $this->Data, true); + } + } + /** * Returns an array representation of the component. * diff --git a/src/ncc/Objects/Package/ExecutionUnit.php b/src/ncc/Objects/Package/ExecutionUnit.php new file mode 100644 index 0000000..75940cc --- /dev/null +++ b/src/ncc/Objects/Package/ExecutionUnit.php @@ -0,0 +1,71 @@ + $this->ExecutionPolicy->toArray($bytecode), + ($bytecode ? Functions::cbc('data') : 'data') => $this->Data, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return static + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->ExecutionPolicy = Functions::array_bc($data, 'execution_policy'); + $object->Data = Functions::array_bc($data, 'data'); + + return $object; + } + + /** + * @return string + */ + public function getID(): string + { + if($this->ID == null) + $this->ID = hash('sha1', $this->ExecutionPolicy->Name); + return $this->ID; + } + + } \ No newline at end of file diff --git a/src/ncc/Objects/Package/Header.php b/src/ncc/Objects/Package/Header.php index e090bc5..16a627e 100644 --- a/src/ncc/Objects/Package/Header.php +++ b/src/ncc/Objects/Package/Header.php @@ -68,6 +68,9 @@ $object->RuntimeConstants = Functions::array_bc($data, 'runtime_constants'); $object->CompilerVersion = Functions::array_bc($data, 'compiler_version'); + if($object->CompilerExtension !== null) + $object->CompilerExtension = Compiler::fromArray($object->CompilerExtension); + return $object; } } \ No newline at end of file diff --git a/src/ncc/Objects/Package/Installer.php b/src/ncc/Objects/Package/Installer.php index cbeb8b0..c368aac 100644 --- a/src/ncc/Objects/Package/Installer.php +++ b/src/ncc/Objects/Package/Installer.php @@ -1,18 +1,81 @@ PreInstall == null && $this->PostInstall == null && + $this->PreUninstall == null && $this->PostUninstall == null && + $this->PreUpdate == null && $this->PostUpdate == null + ) + { + return null; + } + + return [ + ($bytecode ? Functions::cbc('pre_install') : 'pre_install') => ($this->PreInstall == null ? null : $this->PreInstall), + ($bytecode ? Functions::cbc('post_install') : 'post_install') => ($this->PostInstall == null ? null : $this->PostInstall), + ($bytecode ? Functions::cbc('pre_uninstall') : 'pre_uninstall') => ($this->PreUninstall == null ? null : $this->PreUninstall), + ($bytecode? Functions::cbc('post_uninstall') : 'post_uninstall') => ($this->PostUninstall == null ? null : $this->PostUninstall), + ($bytecode? Functions::cbc('pre_update') : 'pre_update') => ($this->PreUpdate == null ? null : $this->PreUpdate), + ($bytecode? Functions::cbc('post_update') : 'post_update') => ($this->PostUpdate == null ? null : $this->PostUpdate) + ]; } /** @@ -20,11 +83,19 @@ * * @param array $data * @return Installer + * @noinspection DuplicatedCode */ public static function fromArray(array $data): self { $object = new self(); + $object->PreInstall = Functions::array_bc($data, 'pre_install'); + $object->PostInstall = Functions::array_bc($data, 'post_install'); + $object->PreUninstall = Functions::array_bc($data, 'pre_uninstall'); + $object->PostUninstall = Functions::array_bc($data, 'post_uninstall'); + $object->PreUpdate = Functions::array_bc($data, 'pre_update'); + $object->PostUpdate = Functions::array_bc($data, 'post_update'); + return $object; } } \ No newline at end of file diff --git a/src/ncc/Objects/Package/MagicBytes.php b/src/ncc/Objects/Package/MagicBytes.php index df54f71..3e86446 100644 --- a/src/ncc/Objects/Package/MagicBytes.php +++ b/src/ncc/Objects/Package/MagicBytes.php @@ -147,6 +147,12 @@ // NCC_PACKAGE1.030140 $magic_bytes .= '40'; } + else + { + // If no type is specified, default to installable only + // NCC_PACKAGE1.030140 + $magic_bytes .= '40'; + } return $magic_bytes; } diff --git a/src/ncc/Objects/Package/MainExecutionPolicy.php b/src/ncc/Objects/Package/MainExecutionPolicy.php deleted file mode 100644 index c005578..0000000 --- a/src/ncc/Objects/Package/MainExecutionPolicy.php +++ /dev/null @@ -1,30 +0,0 @@ -Data === null) return false; - if(hash('sha1', $this->Data) !== $this->Checksum) + if(hash('sha1', $this->Data, true) !== $this->Checksum) return false; return true; } + /** + * Updates the checksum of the resource + * + * @return void + */ + public function updateChecksum(): void + { + $this->Checksum = null; + + if(gettype($this->Data) == 'string') + { + $this->Checksum = hash('sha1', $this->Data, true); + } + } + /** * Returns an array representation of the resource. * diff --git a/src/ncc/Objects/PackageLock.php b/src/ncc/Objects/PackageLock.php new file mode 100644 index 0000000..3e4d055 --- /dev/null +++ b/src/ncc/Objects/PackageLock.php @@ -0,0 +1,194 @@ +PackageLockVersion = Versions::PackageLockVersion; + $this->Packages = []; + } + + /** + * Updates the version and timestamp + * + * @return void + */ + private function update(): void + { + $this->PackageLockVersion = Versions::PackageLockVersion; + $this->LastUpdatedTimestamp = time(); + } + + /** + * @param Package $package + * @param string $install_path + * @return void + */ + public function addPackage(Package $package, string $install_path): void + { + if(!isset($this->Packages[$package->Assembly->Package])) + { + $package_entry = new PackageEntry(); + $package_entry->addVersion($package, $install_path, true); + $package_entry->Name = $package->Assembly->Package; + $this->Packages[$package->Assembly->Package] = $package_entry; + $this->update(); + + return; + } + + $this->Packages[$package->Assembly->Package]->addVersion($package, true); + $this->update(); + } + + /** + * Removes a package version entry, removes the entire entry if there are no installed versions + * + * @param string $package + * @param string $version + * @return bool + */ + public function removePackageVersion(string $package, string $version): bool + { + if(isset($this->Packages[$package])) + { + $r = $this->Packages[$package]->removeVersion($version); + + // Remove the entire package entry if there's no installed versions + if($this->Packages[$package]->getLatestVersion() == null && $r) + { + unset($this->Packages[$package]); + } + + $this->update(); + return $r; + } + + return false; + } + + /** + * Removes an entire package entry + * + * @param string $package + * @return bool + */ + public function removePackage(string $package): bool + { + if(isset($this->Packages[$package])) + { + unset($this->Packages[$package]); + return true; + } + + return false; + } + + /** + * Gets an existing package entry, returns null if no such entry exists + * + * @param string $package + * @return PackageEntry|null + */ + public function getPackage(string $package): ?PackageEntry + { + if(isset($this->Packages[$package])) + { + return $this->Packages[$package]; + } + + return null; + } + + /** + * Returns an array of all packages and their installed versions + * + * @return array + */ + public function getPackages(): array + { + $results = []; + foreach($this->Packages as $package => $entry) + $results[$package] = $entry->getVersions(); + return $results; + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + $package_entries = []; + foreach($this->Packages as $entry) + { + $package_entries[] = $entry->toArray($bytecode); + } + + return [ + ($bytecode ? Functions::cbc('package_lock_version') : 'package_lock_version') => $this->PackageLockVersion, + ($bytecode ? Functions::cbc('last_updated_timestamp') : 'last_updated_timestamp') => $this->LastUpdatedTimestamp, + ($bytecode ? Functions::cbc('packages') : 'packages') => $package_entries + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return static + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $packages = Functions::array_bc($data, 'packages'); + if($packages !== null) + { + foreach($packages as $_datum) + { + $entry = PackageEntry::fromArray($_datum); + $object->Packages[$entry->Name] = $entry; + } + } + + $object->PackageLockVersion = Functions::array_bc($data, 'package_lock_version'); + $object->LastUpdatedTimestamp = Functions::array_bc($data, 'last_updated_timestamp'); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/PackageLock/DependencyEntry.php b/src/ncc/Objects/PackageLock/DependencyEntry.php new file mode 100644 index 0000000..82be14e --- /dev/null +++ b/src/ncc/Objects/PackageLock/DependencyEntry.php @@ -0,0 +1,64 @@ +PackageName = $dependency->Name; + $this->Version = $dependency->Version; + } + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + return [ + ($bytecode ? Functions::cbc('package_name') : 'package_name') => $this->PackageName, + ($bytecode ? Functions::cbc('version') : 'version') => $this->Version, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return DependencyEntry + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->PackageName = Functions::array_bc($data, 'package_name'); + $object->Version = Functions::array_bc($data, 'version'); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/PackageLock/PackageEntry.php b/src/ncc/Objects/PackageLock/PackageEntry.php new file mode 100644 index 0000000..43cf7a9 --- /dev/null +++ b/src/ncc/Objects/PackageLock/PackageEntry.php @@ -0,0 +1,235 @@ +Versions = []; + } + + /** + * Searches and returns a version of the package + * + * @param string $version + * @param bool $throw_exception + * @return VersionEntry|null + * @throws VersionNotFoundException + */ + public function getVersion(string $version, bool $throw_exception=false): ?VersionEntry + { + foreach($this->Versions as $versionEntry) + { + if($versionEntry->Version == $version) + { + return $versionEntry; + } + } + + if($throw_exception) + throw new VersionNotFoundException('The version entry is not found'); + + return null; + } + + /** + * Removes version entry from the package + * + * @param string $version + * @return bool + * @noinspection PhpUnused + */ + public function removeVersion(string $version): bool + { + $count = 0; + $found_node = false; + foreach($this->Versions as $versionEntry) + { + if($versionEntry->Version == $version) + { + $found_node = true; + break; + } + + $count += 1; + } + + if($found_node) + { + unset($this->Versions[$count]); + $this->updateLatestVersion(); + return true; + } + + return false; + } + + /** + * Adds a new version entry to the package, if overwrite is true then + * the entry will be overwritten if it exists, otherwise it will return + * false. + * + * @param Package $package + * @param string $install_path + * @param bool $overwrite + * @return bool + */ + public function addVersion(Package $package, string $install_path, bool $overwrite=false): bool + { + try + { + if ($this->getVersion($package->Assembly->Version) !== null) + { + if (!$overwrite) return false; + $this->removeVersion($package->Assembly->Version); + } + } + catch (VersionNotFoundException $e) + { + unset($e); + } + + $version = new VersionEntry(); + $version->Version = $package->Assembly->Version; + $version->Compiler = $package->Header->CompilerExtension; + $version->ExecutionUnits = $package->ExecutionUnits; + $version->MainExecutionPolicy = $package->MainExecutionPolicy; + $version->Location = $install_path; + + foreach($package->Dependencies as $dependency) + { + $version->Dependencies[] = new DependencyEntry($dependency); + } + + $this->Versions[] = $version; + $this->updateLatestVersion(); + return true; + } + + /** + * Updates and returns the latest version of this package entry + * + * @return void + */ + private function updateLatestVersion(): void + { + $latest_version = null; + foreach($this->Versions as $version) + { + $version = $version->Version; + if($latest_version == null) + { + $latest_version = $version; + continue; + } + if(VersionComparator::compareVersion($version, $latest_version)) + $latest_version = $version; + } + + $this->LatestVersion = $latest_version; + } + + /** + * @return string|null + */ + public function getLatestVersion(): ?string + { + return $this->LatestVersion; + } + + /** + * Returns an array of all versions installed + * + * @return array + */ + public function getVersions(): array + { + $r = []; + + foreach($this->Versions as $version) + { + $r[] = $version->Version; + } + + return $r; + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + $versions = []; + foreach($this->Versions as $version) + { + $versions[] = $version->toArray($bytecode); + } + + return [ + ($bytecode ? Functions::cbc('name') : 'name') => $this->Name, + ($bytecode ? Functions::cbc('latest_version') : 'latest_version') => $this->LatestVersion, + ($bytecode ? Functions::cbc('versions') : 'versions') => $versions, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return PackageEntry + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->Name = Functions::array_bc($data, 'name'); + $object->LatestVersion = Functions::array_bc($data, 'latest_version'); + $versions = Functions::array_bc($data, 'versions'); + + if($versions !== null) + { + foreach($versions as $_datum) + { + $object->Versions[] = VersionEntry::fromArray($_datum); + } + } + + return $object; + } + + } \ No newline at end of file diff --git a/src/ncc/Objects/PackageLock/VersionEntry.php b/src/ncc/Objects/PackageLock/VersionEntry.php new file mode 100644 index 0000000..f512c4a --- /dev/null +++ b/src/ncc/Objects/PackageLock/VersionEntry.php @@ -0,0 +1,126 @@ +Dependencies = []; + $this->ExecutionUnits = []; + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + $dependencies = []; + foreach($this->Dependencies as $dependency) + { + $dependencies[] = $dependency->toArray($bytecode); + } + + $execution_units = []; + foreach($this->ExecutionUnits as $executionUnit) + { + $execution_units[] = $executionUnit->toArray($bytecode); + } + + return [ + ($bytecode ? Functions::cbc('version') : 'version') => $this->Version, + ($bytecode ? Functions::cbc('compiler') : 'compiler') => $this->Compiler->toArray($bytecode), + ($bytecode ? Functions::cbc('dependencies') : 'dependencies') => $dependencies, + ($bytecode ? Functions::cbc('execution_units') : 'execution_units') => $execution_units, + ($bytecode ? Functions::cbc('main_execution_policy') : 'main_execution_policy') => $this->MainExecutionPolicy, + ($bytecode ? Functions::cbc('location') : 'location') => $this->Location, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return VersionEntry + */ + public static function fromArray(array $data): self + { + $object = new self(); + $object->Version = Functions::array_bc($data, 'version'); + $object->Compiler = Compiler::fromArray(Functions::array_bc($data, 'compiler')); + $object->MainExecutionPolicy = Functions::array_bc($data, 'main_execution_policy'); + $object->Location = Functions::array_bc($data, 'location'); + + $dependencies = Functions::array_bc($data, 'dependencies'); + if($dependencies !== null) + { + foreach($dependencies as $_datum) + { + $object->Dependencies[] = DependencyEntry::fromArray($_datum); + } + } + + $execution_units = Functions::array_bc($data, 'execution_units'); + if($execution_units !== null) + { + foreach($execution_units as $_datum) + { + $object->ExecutionUnits[] = ExecutionUnit::fromArray($_datum); + } + } + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ProjectConfiguration.php b/src/ncc/Objects/ProjectConfiguration.php index f2980b1..f361a3e 100644 --- a/src/ncc/Objects/ProjectConfiguration.php +++ b/src/ncc/Objects/ProjectConfiguration.php @@ -1,18 +1,29 @@ Project = new Project(); $this->Assembly = new Assembly(); + $this->ExecutionPolicies = []; $this->Build = new Build(); } @@ -58,13 +84,15 @@ * * @param bool $throw_exception * @return bool + * @throws BuildConfigurationNotFoundException + * @throws InvalidConstantNameException + * @throws InvalidProjectBuildConfiguration * @throws InvalidProjectConfigurationException * @throws InvalidPropertyValueException * @throws RuntimeException + * @throws UndefinedExecutionPolicyException * @throws UnsupportedCompilerExtensionException * @throws UnsupportedExtensionVersionException - * @throws InvalidProjectBuildConfiguration - * @throws InvalidConstantNameException */ public function validate(bool $throw_exception=True): bool { @@ -77,9 +105,170 @@ if(!$this->Build->validate($throw_exception)) return false; + try + { + $this->getRequiredExecutionPolicies(BuildConfigurationValues::AllConfigurations); + } + catch(Exception $e) + { + if($throw_exception) + throw $e; + return false; + } + return true; } + /** + * @param string $name + * @return ExecutionPolicy|null + */ + private function getExecutionPolicy(string $name): ?ExecutionPolicy + { + foreach($this->ExecutionPolicies as $executionPolicy) + { + if($executionPolicy->Name == $name) + return $executionPolicy; + } + + return null; + } + + /** + * Runs a check on the project configuration and determines what policies are required + * + * @param string $build_configuration + * @return array + * @throws BuildConfigurationNotFoundException + * @throws UndefinedExecutionPolicyException + */ + public function getRequiredExecutionPolicies(string $build_configuration=BuildConfigurationValues::DefaultConfiguration): array + { + if($this->ExecutionPolicies == null || count($this->ExecutionPolicies) == 0) + return []; + + $defined_polices = []; + $required_policies = []; + /** @var ExecutionPolicy $execution_policy */ + foreach($this->ExecutionPolicies as $execution_policy) + { + $defined_polices[] = $execution_policy->Name; + $execution_policy->validate(); + } + + // Check the installer by batch + if($this->Installer !== null) + { + $array_rep = $this->Installer->toArray(); + /** @var string[] $value */ + foreach($array_rep as $key => $value) + { + if($value == null || count($value) == 0) + continue; + + foreach($value as $unit) + { + if(!in_array($unit, $defined_polices)) + throw new UndefinedExecutionPolicyException('The property \'' . $key . '\' in the project configuration calls for an undefined execution policy \'' . $unit . '\''); + if(!in_array($unit, $required_policies)) + $required_policies[] = $unit; + } + } + } + + if($this->Build->PreBuild !== null && count($this->Build->PostBuild) > 0) + { + foreach($this->Build->PostBuild as $unit) + { + if(!in_array($unit, $defined_polices)) + throw new UndefinedExecutionPolicyException('The property \'build.pre_build\' in the project configuration calls for an undefined execution policy \'' . $unit . '\''); + if(!in_array($unit, $required_policies)) + $required_policies[] = $unit; + } + } + + if($this->Build->PostBuild !== null && count($this->Build->PostBuild) > 0) + { + foreach($this->Build->PostBuild as $unit) + { + if(!in_array($unit, $defined_polices)) + throw new UndefinedExecutionPolicyException('The property \'build.pre_build\' in the project configuration calls for an undefined execution policy \'' . $unit . '\''); + if(!in_array($unit, $required_policies)) + $required_policies[] = $unit; + } + } + + switch($build_configuration) + { + case BuildConfigurationValues::AllConfigurations: + /** @var BuildConfiguration $configuration */ + foreach($this->Build->Configurations as $configuration) + { + foreach($this->processBuildPolicies($configuration, $defined_polices) as $policy) + { + if(!in_array($policy, $required_policies)) + $required_policies[] = $policy; + } + } + break; + + default: + $configuration = $this->Build->getBuildConfiguration($build_configuration); + foreach($this->processBuildPolicies($configuration, $defined_polices) as $policy) + { + if(!in_array($policy, $required_policies)) + $required_policies[] = $policy; + } + break; + } + + foreach($required_policies as $policy) + { + $execution_policy = $this->getExecutionPolicy($policy); + if($execution_policy->ExitHandlers !== null) + { + if( + $execution_policy->ExitHandlers->Success !== null && + $execution_policy->ExitHandlers->Success->Run !== null + ) + { + if(!in_array($execution_policy->ExitHandlers->Success->Run, $defined_polices)) + throw new UndefinedExecutionPolicyException('The execution policy \'' . $execution_policy->Name . '\' Success exit handler points to a undefined execution policy \'' . $execution_policy->ExitHandlers->Success->Run . '\''); + + if(!in_array($execution_policy->ExitHandlers->Success->Run, $required_policies)) + $required_policies[] = $execution_policy->ExitHandlers->Success->Run; + } + + if( + $execution_policy->ExitHandlers->Warning !== null && + $execution_policy->ExitHandlers->Warning->Run !== null + ) + { + if(!in_array($execution_policy->ExitHandlers->Warning->Run, $defined_polices)) + throw new UndefinedExecutionPolicyException('The execution policy \'' . $execution_policy->Name . '\' Warning exit handler points to a undefined execution policy \'' . $execution_policy->ExitHandlers->Warning->Run . '\''); + + if(!in_array($execution_policy->ExitHandlers->Warning->Run, $required_policies)) + $required_policies[] = $execution_policy->ExitHandlers->Warning->Run; + } + + if( + $execution_policy->ExitHandlers->Error !== null && + $execution_policy->ExitHandlers->Error->Run !== null + ) + { + if(!in_array($execution_policy->ExitHandlers->Error->Run, $defined_polices)) + throw new UndefinedExecutionPolicyException('The execution policy \'' . $execution_policy->Name . '\' Error exit handler points to a undefined execution policy \'' . $execution_policy->ExitHandlers->Error->Run . '\''); + + if(!in_array($execution_policy->ExitHandlers->Error->Run, $required_policies)) + $required_policies[] = $execution_policy->ExitHandlers->Error->Run; + } + } + + } + + return $required_policies; + } + /** * Returns an array representation of the object * @@ -88,9 +277,15 @@ */ public function toArray(bool $bytecode=false): array { + $execution_policies = []; + foreach($this->ExecutionPolicies as $executionPolicy) + { + $execution_policies[$executionPolicy->Name] = $executionPolicy->toArray($bytecode); + } return [ ($bytecode ? Functions::cbc('project') : 'project') => $this->Project->toArray($bytecode), ($bytecode ? Functions::cbc('assembly') : 'assembly') => $this->Assembly->toArray($bytecode), + ($bytecode ? Functions::cbc('execution_policies') : 'execution_policies') => $execution_policies, ($bytecode ? Functions::cbc('build') : 'build') => $this->Build->toArray($bytecode), ]; } @@ -128,7 +323,26 @@ $ProjectConfigurationObject->Project = Project::fromArray(Functions::array_bc($data, 'project')); $ProjectConfigurationObject->Assembly = Assembly::fromArray(Functions::array_bc($data, 'assembly')); + $ProjectConfigurationObject->ExecutionPolicies = Functions::array_bc($data, 'execution_policies'); $ProjectConfigurationObject->Build = Build::fromArray(Functions::array_bc($data, 'build')); + $ProjectConfigurationObject->Installer = Functions::array_bc($data, 'installer'); + + if($ProjectConfigurationObject->Installer !== null) + $ProjectConfigurationObject->Installer = Installer::fromArray($ProjectConfigurationObject->Installer); + + if($ProjectConfigurationObject->ExecutionPolicies == null) + { + $ProjectConfigurationObject->ExecutionPolicies = []; + } + else + { + $policies = []; + foreach($ProjectConfigurationObject->ExecutionPolicies as $policy) + { + $policies[] = ExecutionPolicy::fromArray($policy); + } + $ProjectConfigurationObject->ExecutionPolicies = $policies; + } return $ProjectConfigurationObject; } @@ -140,10 +354,45 @@ * @return ProjectConfiguration * @throws FileNotFoundException * @throws MalformedJsonException + * @throws AccessDeniedException + * @throws IOException * @noinspection PhpUnused */ public static function fromFile(string $path): ProjectConfiguration { return ProjectConfiguration::fromArray(Functions::loadJsonFile($path, Functions::FORCE_ARRAY)); } + + /** + * @param BuildConfiguration $configuration + * @param array $defined_polices + * @return array + * @throws UndefinedExecutionPolicyException + */ + private function processBuildPolicies(BuildConfiguration $configuration, array $defined_polices): array + { + $required_policies = []; + + if ($configuration->PreBuild !== null && count($configuration->PreBuild) > 0) + { + foreach ($configuration->PreBuild as $unit) + { + if (!in_array($unit, $defined_polices)) + throw new UndefinedExecutionPolicyException('The property \'pre_build\' in the build configuration \'' . $configuration->Name . '\' calls for an undefined execution policy \'' . $unit . '\''); + $required_policies[] = $unit; + } + } + + if ($configuration->PostBuild !== null && count($configuration->PostBuild) > 0) + { + foreach ($configuration->PostBuild as $unit) + { + if (!in_array($unit, $defined_polices)) + throw new UndefinedExecutionPolicyException('The property \'pre_build\' in the build configuration \'' . $configuration->Name . '\' calls for an undefined execution policy \'' . $unit . '\''); + $required_policies[] = $unit; + } + } + + return $required_policies; + } } \ No newline at end of file diff --git a/src/ncc/Objects/ProjectConfiguration/Build.php b/src/ncc/Objects/ProjectConfiguration/Build.php index cf45987..71caa85 100644 --- a/src/ncc/Objects/ProjectConfiguration/Build.php +++ b/src/ncc/Objects/ProjectConfiguration/Build.php @@ -4,6 +4,7 @@ namespace ncc\Objects\ProjectConfiguration; + use ncc\Abstracts\Options\BuildConfigurationValues; use ncc\Exceptions\BuildConfigurationNotFoundException; use ncc\Exceptions\InvalidConstantNameException; use ncc\Exceptions\InvalidProjectBuildConfiguration; @@ -51,6 +52,13 @@ */ public $Scope; + /** + * The execution policy to use as the main execution point + * + * @var string|null + */ + public $Main; + /** * An array of constants to define by default * @@ -58,6 +66,20 @@ */ public $DefineConstants; + /** + * An array of execution policies to execute pre build + * + * @var string[] + */ + public $PreBuild; + + /** + * An array of execution policies to execute post build + * + * @var string[] + */ + public $PostBuild; + /** * An array of dependencies that are required by default * @@ -125,6 +147,7 @@ * Returns an array of all the build configurations defined in the project configuration * * @return array + * @noinspection PhpUnused */ public function getBuildConfigurations(): array { @@ -148,6 +171,9 @@ */ public function getBuildConfiguration(string $name): BuildConfiguration { + if($name == BuildConfigurationValues::DefaultConfiguration) + $name = $this->DefaultConfiguration; + foreach($this->Configurations as $configuration) { if($configuration->Name == $name) @@ -174,7 +200,10 @@ $ReturnResults[($bytecode ? Functions::cbc('exclude_files') : 'exclude_files')] = $this->ExcludeFiles; $ReturnResults[($bytecode ? Functions::cbc('options') : 'options')] = $this->Options; $ReturnResults[($bytecode ? Functions::cbc('scope') : 'scope')] = $this->Scope; + $ReturnResults[($bytecode ? Functions::cbc('main') : 'main')] = $this->Main; $ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants; + $ReturnResults[($bytecode ? Functions::cbc('pre_build') : 'pre_build')] = $this->PreBuild; + $ReturnResults[($bytecode ? Functions::cbc('post_build') : 'post_build')] = $this->PostBuild; $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')] = []; foreach($this->Dependencies as $dependency) @@ -207,7 +236,10 @@ $BuildObject->ExcludeFiles = (Functions::array_bc($data, 'exclude_files') ?? []); $BuildObject->Options = (Functions::array_bc($data, 'options') ?? []); $BuildObject->Scope = Functions::array_bc($data, 'scope'); + $BuildObject->Main = Functions::array_bc($data, 'main'); $BuildObject->DefineConstants = (Functions::array_bc($data, 'define_constants') ?? []); + $BuildObject->PreBuild = (Functions::array_bc($data, 'pre_build') ?? []); + $BuildObject->PostBuild = (Functions::array_bc($data, 'post_build') ?? []); if(Functions::array_bc($data, 'dependencies') !== null) { diff --git a/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php b/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php index cc80ef6..45a8bf5 100644 --- a/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php +++ b/src/ncc/Objects/ProjectConfiguration/BuildConfiguration.php @@ -47,6 +47,20 @@ */ public $ExcludeFiles; + /** + * An array of policies to execute pre-building the package + * + * @var string[]|string + */ + public $PreBuild; + + /** + * An array of policies to execute post-building the package + * + * @var string + */ + public $PostBuild; + /** * Dependencies required for the build configuration, cannot conflict with the * default dependencies @@ -64,6 +78,8 @@ $this->OutputPath = 'build'; $this->DefineConstants = []; $this->ExcludeFiles = []; + $this->PreBuild = []; + $this->PostBuild = []; $this->Dependencies = []; } @@ -84,6 +100,7 @@ $ReturnResults[($bytecode ? Functions::cbc('output_path') : 'output_path')] = $this->OutputPath; $ReturnResults[($bytecode ? Functions::cbc('define_constants') : 'define_constants')] = $this->DefineConstants; $ReturnResults[($bytecode ? Functions::cbc('exclude_files') : 'exclude_files')] = $this->ExcludeFiles; + $ReturnResults[($bytecode ? Functions::cbc('pre_build') : 'pre_build')] = $this->PreBuild; $ReturnResults[($bytecode ? Functions::cbc('dependencies') : 'dependencies')] = []; foreach($this->Dependencies as $dependency) @@ -105,36 +122,30 @@ $BuildConfigurationObject = new BuildConfiguration(); if(Functions::array_bc($data, 'name') !== null) - { $BuildConfigurationObject->Name = Functions::array_bc($data, 'name'); - } if(Functions::array_bc($data, 'options') !== null) - { $BuildConfigurationObject->Options = Functions::array_bc($data, 'options'); - } if(Functions::array_bc($data, 'output_path') !== null) - { $BuildConfigurationObject->OutputPath = Functions::array_bc($data, 'output_path'); - } if(Functions::array_bc($data, 'define_constants') !== null) - { $BuildConfigurationObject->DefineConstants = Functions::array_bc($data, 'define_constants'); - } if(Functions::array_bc($data, 'exclude_files') !== null) - { $BuildConfigurationObject->ExcludeFiles = Functions::array_bc($data, 'exclude_files'); - } + + if(Functions::array_bc($data, 'pre_build') !== null) + $BuildConfigurationObject->PreBuild = Functions::array_bc($data, 'pre_build'); + + if(Functions::array_bc($data, 'post_build') !== null) + $BuildConfigurationObject->PostBuild = Functions::array_bc($data, 'post_build'); if(Functions::array_bc($data, 'dependencies') !== null) { foreach(Functions::array_bc($data, 'dependencies') as $item) - { $BuildConfigurationObject->Dependencies[] = Dependency::fromArray($item); - } } return $BuildConfigurationObject; diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php new file mode 100644 index 0000000..b627a30 --- /dev/null +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy.php @@ -0,0 +1,98 @@ + $this->Name, + ($bytecode ? Functions::cbc('runner') : 'runner') => $this->Runner, + ($bytecode ? Functions::cbc('message') : 'message') => $this->Message, + ($bytecode ? Functions::cbc('exec') : 'exec') => $this->Execute?->toArray($bytecode), + ($bytecode ? Functions::cbc('exit_handlers') : 'exit_handlers') => $this->ExitHandlers?->toArray($bytecode), + ]; + } + + /** + * @param array $data + * @return ExecutionPolicy + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->Name = Functions::array_bc($data, 'name'); + $object->Runner = Functions::array_bc($data, 'runner'); + $object->Message = Functions::array_bc($data, 'message'); + $object->Execute = Functions::array_bc($data, 'exec'); + $object->ExitHandlers = Functions::array_bc($data, 'exit_handlers'); + + if($object->Execute !== null) + $object->Execute = Execute::fromArray($object->Execute); + + if($object->ExitHandlers !== null) + $object->ExitHandlers = ExitHandlers::fromArray($object->ExitHandlers); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php new file mode 100644 index 0000000..0cc164a --- /dev/null +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/Execute.php @@ -0,0 +1,103 @@ +Tty = false; + $this->Silent = false; + $this->Timeout = null; + $this->WorkingDirectory = "%CWD%"; + } + + /** + * Returns an array representation of the object + * + * @param bool $bytecode + * @return array + */ + public function toArray(bool $bytecode=false): array + { + return [ + ($bytecode ? Functions::cbc('target') : 'target') => $this->Target, + ($bytecode ? Functions::cbc('working_directory') : 'working_directory') => $this->WorkingDirectory, + ($bytecode ? Functions::cbc('options') : 'options') => $this->Options, + ($bytecode ? Functions::cbc('silent') : 'silent') => $this->Silent, + ($bytecode ? Functions::cbc('tty') : 'tty') => $this->Tty, + ($bytecode ? Functions::cbc('timeout') : 'timeout') => $this->Timeout + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return Execute + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->Target = Functions::array_bc($data, 'target'); + $object->WorkingDirectory = Functions::array_bc($data, 'working_directory'); + $object->Options = Functions::array_bc($data, 'options'); + $object->Silent = Functions::array_bc($data, 'silent'); + $object->Tty = Functions::array_bc($data, 'tty'); + $object->Timeout = Functions::array_bc($data, 'timeout'); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php new file mode 100644 index 0000000..8d78bcc --- /dev/null +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandle.php @@ -0,0 +1,76 @@ + $this->Message, + ($bytecode ? Functions::cbc('end_process') : 'end_process') => $this->EndProcess, + ($bytecode ? Functions::cbc('run') : 'run') => $this->Run, + ($bytecode ? Functions::cbc('exit_code') : 'exit_code') => $this->ExitCode, + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return ExitHandle + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->Message = Functions::array_bc($data, 'message'); + $object->EndProcess = Functions::array_bc($data, 'end_process'); + $object->Run = Functions::array_bc($data, 'run'); + $object->ExitCode = Functions::array_bc($data, 'exit_code'); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandlers.php b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandlers.php new file mode 100644 index 0000000..28a73b2 --- /dev/null +++ b/src/ncc/Objects/ProjectConfiguration/ExecutionPolicy/ExitHandlers.php @@ -0,0 +1,71 @@ + $this->Success?->toArray($bytecode), + ($bytecode ? Functions::cbc('warning') : 'warning') => $this->Warning?->toArray($bytecode), + ($bytecode ? Functions::cbc('error') : 'error') => $this->Error?->toArray($bytecode), + ]; + } + + /** + * Constructs object from an array representation + * + * @param array $data + * @return ExitHandlers + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->Success = Functions::array_bc($data, 'success'); + if($object->Success !== null) + $object->Success = ExitHandle::fromArray($object->Success); + + $object->Warning = Functions::array_bc($data, 'warning'); + if($object->Warning !== null) + $object->Warning = ExitHandle::fromArray($object->Warning); + + $object->Error = Functions::array_bc($data, 'error'); + if($object->Error !== null) + $object->Error = ExitHandle::fromArray($object->Error); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Objects/ProjectConfiguration/Installer.php b/src/ncc/Objects/ProjectConfiguration/Installer.php new file mode 100644 index 0000000..30dda88 --- /dev/null +++ b/src/ncc/Objects/ProjectConfiguration/Installer.php @@ -0,0 +1,88 @@ + $this->PreInstall, + ($bytecode? Functions::cbc('post_install') : 'post_install') => $this->PostInstall, + ($bytecode? Functions::cbc('pre_uninstall') : 'pre_uninstall') => $this->PostUninstall, + ($bytecode? Functions::cbc('post_uninstall') : 'post_uninstall') => $this->PostUninstall, + ($bytecode? Functions::cbc('pre_update') : 'pre_update') => $this->PreUpdate, + ($bytecode? Functions::cbc('post_update') : 'post_update') => $this->PostUpdate + ]; + } + + /** + * @param array $data + * @return Installer + */ + public static function fromArray(array $data): self + { + $object = new self(); + + $object->PreInstall = Functions::array_bc($data, 'pre_install'); + $object->PostInstall = Functions::array_bc($data, 'post_install'); + $object->PreUninstall = Functions::array_bc($data, 'pre_uninstall'); + $object->PostUninstall = Functions::array_bc($data, 'post_uninstall'); + $object->PreUpdate = Functions::array_bc($data, 'pre_update'); + $object->PostUpdate = Functions::array_bc($data, 'post_update'); + + return $object; + } + } \ No newline at end of file diff --git a/src/ncc/Utilities/Base64.php b/src/ncc/Utilities/Base64.php index f1d7b5e..9c75af3 100644 --- a/src/ncc/Utilities/Base64.php +++ b/src/ncc/Utilities/Base64.php @@ -17,6 +17,10 @@ */ public static function encode(string $string): string { + // Builtin function is faster than raw implementation + if(function_exists('base64_encode')) + return base64_encode($string); + $base64 = str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); $bit_pattern = ''; $padding = 0; @@ -54,6 +58,9 @@ */ public static function decode(string $string): string { + if(function_exists('base64_decode')) + return base64_encode($string); + $base64 = str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'); $bit_pattern = ''; $padding = substr_count(substr(strrev($string), 0, 2), '='); diff --git a/src/ncc/Utilities/Console.php b/src/ncc/Utilities/Console.php index 870eeb9..30bd0ff 100644 --- a/src/ncc/Utilities/Console.php +++ b/src/ncc/Utilities/Console.php @@ -4,10 +4,17 @@ use Exception; use ncc\Abstracts\ConsoleColors; + use ncc\Abstracts\LogLevel; + use ncc\CLI\Main; use ncc\ncc; class Console { + /** + * @var int + */ + private static $largestTickLength = 0; + /** * Inline Progress bar, created by dealnews.com. * @@ -23,6 +30,20 @@ if(!ncc::cliMode()) return; + if(Main::getLogLevel() !== null) + { + switch(Main::getLogLevel()) + { + case LogLevel::Verbose: + case LogLevel::Debug: + case LogLevel::Silent: + return; + + default: + break; + } + } + static $start_time; // if we go over our bound, just ignore it @@ -44,6 +65,7 @@ $status_bar.="="; } + /** @noinspection PhpRedundantOptionalArgumentInspection */ $disp=number_format($perc*100, 0); $status_bar.=" ] $disp% $value/$total"; @@ -74,18 +96,54 @@ } } + /** + * Appends a verbose prefix to the message + * + * @param string $log_level + * @param string $input + * @return string + */ + private static function setPrefix(string $log_level, string $input): string + { + $input = match ($log_level) { + LogLevel::Verbose => self::formatColor('VRB:', ConsoleColors::LightCyan) . " $input", + LogLevel::Debug => self::formatColor('DBG:', ConsoleColors::LightMagenta) . " $input", + LogLevel::Info => self::formatColor('INF:', ConsoleColors::White) . " $input", + LogLevel::Warning => self::formatColor('WRN:', ConsoleColors::Yellow) . " $input", + LogLevel::Error => self::formatColor('ERR:', ConsoleColors::LightRed) . " $input", + LogLevel::Fatal => self::formatColor('FTL:', ConsoleColors::LightRed) . " $input", + default => self::formatColor('MSG:', ConsoleColors::Default) . " $input", + }; + + $tick_time = (string)microtime(true); + if(strlen($tick_time) > self::$largestTickLength) + self::$largestTickLength = strlen($tick_time); + if(strlen($tick_time) < self::$largestTickLength) + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $tick_time = str_pad($tick_time, (strlen($tick_time) + (self::$largestTickLength - strlen($tick_time))), ' ', STR_PAD_RIGHT); + + return '[' . $tick_time . ' - ' . date('TH:i:sP') . '] ' . $input; + } + /** * Simple output function * * @param string $message * @param bool $newline + * @param bool $no_prefix * @return void */ - public static function out(string $message, bool $newline=true): void + public static function out(string $message, bool $newline=true, bool $no_prefix=false): void { if(!ncc::cliMode()) return; + if(Main::getLogLevel() !== null && !Resolver::checkLogLevel(LogLevel::Info, Main::getLogLevel())) + return; + + if(Main::getLogLevel() !== null && Resolver::checkLogLevel(LogLevel::Verbose, Main::getLogLevel()) && !$no_prefix) + $message = self::setPrefix(LogLevel::Info, $message); + if($newline) { print($message . PHP_EOL); @@ -95,6 +153,58 @@ print($message); } + /** + * Output debug message + * + * @param string $message + * @param bool $newline + * @return void + */ + public static function outDebug(string $message, bool $newline=true): void + { + if(!ncc::cliMode()) + return; + + if(Main::getLogLevel() !== null && !Resolver::checkLogLevel(LogLevel::Debug, Main::getLogLevel())) + return; + + $backtrace = null; + if(function_exists('debug_backtrace')) + $backtrace = debug_backtrace(); + $trace_msg = null; + if($backtrace !== null && isset($backtrace[1])) + { + $trace_msg = Console::formatColor($backtrace[1]['class'], ConsoleColors::LightGray); + $trace_msg .= $backtrace[1]['type']; + $trace_msg .= Console::formatColor($backtrace[1]['function'] . '()', ConsoleColors::LightGreen); + $trace_msg .= ' > '; + } + + /** @noinspection PhpUnnecessaryStringCastInspection */ + $message = self::setPrefix(LogLevel::Debug, (string)$trace_msg . $message); + + self::out($message, $newline, true); + } + + /** + * Output debug message + * + * @param string $message + * @param bool $newline + * @return void + */ + public static function outVerbose(string $message, bool $newline=true): void + { + if(!ncc::cliMode()) + return; + + if(Main::getLogLevel() !== null && !Resolver::checkLogLevel(LogLevel::Verbose, Main::getLogLevel())) + return; + + self::out(self::setPrefix(LogLevel::Verbose, $message), $newline, true); + } + + /** * Formats the text to have a different color and returns the formatted value * @@ -105,6 +215,11 @@ */ public static function formatColor(string $input, string $color_code, bool $persist=true): string { + if(Main::getArgs() !== null && isset(Main::getArgs()['no-color'])) + { + return $input; + } + if($persist) { return $color_code . $input . ConsoleColors::Default; @@ -125,6 +240,15 @@ if(!ncc::cliMode()) return; + if(Main::getLogLevel() !== null && !Resolver::checkLogLevel(LogLevel::Warning, Main::getLogLevel())) + return; + + if(Main::getLogLevel() !== null && Resolver::checkLogLevel(LogLevel::Verbose, Main::getLogLevel())) + { + self::out(self::setPrefix(LogLevel::Warning, $message), $newline, true); + return; + } + self::out(self::formatColor('Warning: ', ConsoleColors::Yellow) . $message, $newline); } @@ -141,7 +265,17 @@ if(!ncc::cliMode()) return; - self::out(self::formatColor(ConsoleColors::Red, 'Error: ') . $message, $newline); + if(Main::getLogLevel() !== null && !Resolver::checkLogLevel(LogLevel::Error, Main::getLogLevel())) + return; + + if(Main::getLogLevel() !== null && Resolver::checkLogLevel(LogLevel::Verbose, Main::getLogLevel())) + { + self::out(self::setPrefix(LogLevel::Error, $message), $newline, true); + } + else + { + self::out(self::formatColor(ConsoleColors::Red, 'Error: ') . $message, $newline); + } if($exit_code !== null) { @@ -162,11 +296,12 @@ if(!ncc::cliMode()) return; - if(strlen($message) > 0) + if(strlen($message) > 0 && Resolver::checkLogLevel(LogLevel::Error, Main::getLogLevel())) { - self::out(self::formatColor('Error: ' . $message, ConsoleColors::Red)); + self::out(PHP_EOL . self::formatColor('Error: ', ConsoleColors::Red) . $message); } + Console::out(PHP_EOL . '===== Exception Details ====='); self::outExceptionDetails($e); if($exit_code !== null) @@ -189,6 +324,39 @@ $trace_header = self::formatColor($e->getFile() . ':' . $e->getLine(), ConsoleColors::Magenta); $trace_error = self::formatColor('error: ', ConsoleColors::Red); self::out($trace_header . ' ' . $trace_error . $e->getMessage()); + self::out(sprintf('Error code: %s', $e->getCode())); + $trace = $e->getTrace(); + if(count($trace) > 1) + { + self::out('Stack Trace:'); + foreach($trace as $item) + { + self::out( ' - ' . self::formatColor($item['file'], ConsoleColors::Red) . ':' . $item['line']); + } + } + + if(Main::getArgs() !== null) + { + if(isset(Main::getArgs()['dbg-ex'])) + { + try + { + $dump = [ + 'constants' => ncc::getConstants(), + 'exception' => Functions::exceptionToArray($e) + ]; + IO::fwrite(getcwd() . DIRECTORY_SEPARATOR . time() . '.json', json_encode($dump, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT), 0777); + } + catch (Exception $e) + { + self::outWarning('Cannot dump exception details, ' . $e->getMessage()); + } + } + else + { + self::out('You can pass on \'--dbg-ex\' option to dump the exception details to a json file'); + } + } } /** diff --git a/src/ncc/Utilities/Functions.php b/src/ncc/Utilities/Functions.php index e0b74d9..1838950 100644 --- a/src/ncc/Utilities/Functions.php +++ b/src/ncc/Utilities/Functions.php @@ -2,9 +2,22 @@ namespace ncc\Utilities; + use Exception; + use ncc\Abstracts\Runners; + use ncc\Abstracts\Scopes; + use ncc\Classes\PhpExtension\Runner; + use ncc\Exceptions\AccessDeniedException; use ncc\Exceptions\FileNotFoundException; + use ncc\Exceptions\InvalidScopeException; + use ncc\Exceptions\IOException; use ncc\Exceptions\MalformedJsonException; + use ncc\Exceptions\UnsupportedRunnerException; + use ncc\Managers\CredentialManager; + use ncc\Managers\PackageLockManager; use ncc\Objects\CliHelpSection; + use ncc\Objects\Package\ExecutionUnit; + use ncc\Objects\ProjectConfiguration\ExecutionPolicy; + use ncc\ThirdParty\Symfony\Filesystem\Filesystem; /** * @author Zi Xing Narrakas @@ -22,11 +35,15 @@ * Calculates a byte-code representation of the input using CRC32 * * @param string $input - * @return int + * @return string */ - public static function cbc(string $input): int + public static function cbc(string $input): string { - return hexdec(hash('crc32', $input, true)); + $cache = RuntimeCache::get("cbc_$input"); + if($cache !== null) + return $cache; + + return RuntimeCache::set("cbc_$input", hash('crc32', $input, true)); } /** @@ -36,6 +53,7 @@ * @param array $data * @param string $select * @return mixed|null + * @noinspection PhpMissingReturnTypeInspection */ public static function array_bc(array $data, string $select) { @@ -54,7 +72,9 @@ * @param string $path * @param int $flags * @return mixed + * @throws AccessDeniedException * @throws FileNotFoundException + * @throws IOException * @throws MalformedJsonException * @noinspection PhpMissingReturnTypeInspection */ @@ -65,7 +85,7 @@ throw new FileNotFoundException($path); } - return self::loadJson(file_get_contents($path), $flags); + return self::loadJson(IO::fread($path), $flags); } /** @@ -98,6 +118,7 @@ * @return string * @throws MalformedJsonException * @noinspection PhpMissingParamTypeInspection + * @noinspection PhpUnusedLocalVariableInspection */ public static function encodeJson($value, int $flags=0): string { @@ -123,7 +144,7 @@ * @return void * @throws MalformedJsonException */ - public static function encodeJsonFile($value, string $path, int $flags=0) + public static function encodeJsonFile($value, string $path, int $flags=0): void { file_put_contents($path, self::encodeJson($value, $flags)); } @@ -160,16 +181,19 @@ * @param string $copyright * @param bool $basic_ascii * @return string + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException */ public static function getBanner(string $version, string $copyright, bool $basic_ascii=false): string { if($basic_ascii) { - $banner = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'banner_basic'); + $banner = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'banner_basic'); } else { - $banner = file_get_contents(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'banner_extended'); + $banner = IO::fread(__DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'banner_extended'); } $banner_version = str_pad($version, 21); @@ -219,26 +243,125 @@ } /** - * Converts the input string into a Bas64 encoding before returning it as a - * byte representation - * - * @param string $string - * @return string + * @param string $path + * @param ExecutionPolicy $policy + * @return ExecutionUnit + * @throws UnsupportedRunnerException + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException */ - public static function byteEncode(string $string): string + public static function compileRunner(string $path, ExecutionPolicy $policy): ExecutionUnit { - return convert_uuencode(Base64::encode($string)); + return match (strtolower($policy->Runner)) { + Runners::php => Runner::processUnit($path, $policy), + default => throw new UnsupportedRunnerException('The runner \'' . $policy->Runner . '\' is not supported'), + }; } /** - * Decodes the input string back into the normal string representation that was encoded - * by the byteEncode() function + * Returns an array representation of the exception * - * @param string $string + * @param Exception $e + * @return array + */ + public static function exceptionToArray(Exception $e): array + { + $exception = [ + 'message' => $e->getMessage(), + 'code' => $e->getCode(), + 'file' => $e->getFile(), + 'line' => $e->getLine(), + 'trace' => null, + 'trace_string' => $e->getTraceAsString(), + ]; + + if($e->getPrevious() !== null) + { + $exception['trace'] = self::exceptionToArray($e); + } + + return $exception; + } + + /** + * Takes the input bytes and converts it to a readable unit representation + * + * @param int $bytes + * @param int $decimals * @return string */ - public static function byteDecode(string $string): string + public static function b2u(int $bytes, int $decimals=2): string { - return base64_decode(convert_uudecode($string)); + $size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB'); + $factor = floor((strlen($bytes) - 1) / 3); + return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor]; + } + + /** + * Initializes NCC files + * + * @return void + * @throws AccessDeniedException + * @throws InvalidScopeException + */ + public static function initializeFiles(): void + { + if(Resolver::resolveScope() !== Scopes::System) + throw new AccessDeniedException('Cannot initialize NCC files, insufficient permissions'); + + Console::outVerbose('Initializing NCC files'); + + $filesystem = new Filesystem(); + if(!$filesystem->exists(PathFinder::getDataPath(Scopes::System))) + { + Console::outDebug(sprintf('Initializing %s', PathFinder::getDataPath(Scopes::System))); + $filesystem->mkdir(PathFinder::getDataPath(Scopes::System), 0755); + } + + if(!$filesystem->exists(PathFinder::getCachePath(Scopes::System))) + { + Console::outDebug(sprintf('Initializing %s', PathFinder::getCachePath(Scopes::System))); + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $filesystem->mkdir(PathFinder::getCachePath(Scopes::System), 0777); + } + + if(!$filesystem->exists(PathFinder::getRunnerPath(Scopes::System))) + { + Console::outDebug(sprintf('Initializing %s', PathFinder::getRunnerPath(Scopes::System))); + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $filesystem->mkdir(PathFinder::getRunnerPath(Scopes::System), 0755); + } + + if(!$filesystem->exists(PathFinder::getPackagesPath(Scopes::System))) + { + Console::outDebug(sprintf('Initializing %s', PathFinder::getPackagesPath(Scopes::System))); + /** @noinspection PhpRedundantOptionalArgumentInspection */ + $filesystem->mkdir(PathFinder::getPackagesPath(Scopes::System), 0755); + } + + // Create credential store if needed + try + { + Console::outVerbose('Processing Credential Store'); + $credential_manager = new CredentialManager(); + $credential_manager->constructStore(); + } + catch (Exception $e) + { + Console::outError('Cannot construct credential store, ' . $e->getMessage() . ' (Error Code: ' . $e->getCode() . ')'); + } + + // Create package lock if needed + try + { + Console::outVerbose('Processing Package Lock'); + $package_manager = new PackageLockManager(); + $package_manager->constructLockFile(); + } + catch (Exception $e) + { + Console::outError('Cannot construct Package Lock, ' . $e->getMessage() . ' (Error Code: ' . $e->getCode() . ')'); + } } } \ No newline at end of file diff --git a/src/ncc/Utilities/IO.php b/src/ncc/Utilities/IO.php new file mode 100644 index 0000000..7b9c5be --- /dev/null +++ b/src/ncc/Utilities/IO.php @@ -0,0 +1,97 @@ +getPath())) + { + throw new IOException(sprintf('Attempted to write data to a directory instead of a file: (%s)', $uri)); + } + + Console::outDebug(sprintf('writing %s of data to %s', Functions::b2u(strlen($data)), $uri)); + $file = new SplFileObject($uri, $mode); + + if (!$file->flock(LOCK_EX | LOCK_NB)) + { + throw new IOException(sprintf('Unable to obtain lock on file: (%s)', $uri)); + } + elseif (!$file->fwrite($data)) + { + throw new IOException(sprintf('Unable to write content to file: (%s)... to (%s)', substr($data,0,25), $uri)); + } + elseif (!$file->flock(LOCK_UN)) + { + throw new IOException(sprintf('Unable to remove lock on file: (%s)', $uri)); + } + elseif (!@chmod($uri, $perms)) + { + throw new IOException(sprintf('Unable to chmod: (%s) to (%s)', $uri, $perms)); + } + } + + /** + * Attempts to read the specified file + * + * @param string $uri + * @param string $mode + * @param int|null $length + * @return string + * @throws AccessDeniedException + * @throws FileNotFoundException + * @throws IOException + */ + public static function fread(string $uri, string $mode='r', ?int $length=null): string + { + $fileInfo = new SplFileInfo($uri); + + if(!is_dir($fileInfo->getPath())) + { + throw new IOException(sprintf('Attempted to read data from a directory instead of a file: (%s)', $uri)); + } + + if(!file_exists($uri)) + { + throw new FileNotFoundException(sprintf('Cannot find file %s', $uri)); + } + + if(!is_readable($uri)) + { + throw new AccessDeniedException(sprintf('Insufficient permissions to read %s', $uri)); + } + + $file = new SplFileObject($uri, $mode); + if($length == null) + { + $length = $file->getSize(); + } + + if($length == 0) + { + return (string)null; + } + + Console::outDebug(sprintf('reading %s', $uri)); + return $file->fread($length); + } + } \ No newline at end of file diff --git a/src/ncc/Utilities/PathFinder.php b/src/ncc/Utilities/PathFinder.php index 373c0fa..5e08303 100644 --- a/src/ncc/Utilities/PathFinder.php +++ b/src/ncc/Utilities/PathFinder.php @@ -145,49 +145,16 @@ } /** - * Returns the path where temporary files are stored + * Returns the path where Runner bin files are located and installed * * @param string $scope * @param bool $win32 * @return string * @throws InvalidScopeException */ - public static function getTmpPath(string $scope=Scopes::Auto, bool $win32=false): string + public static function getRunnerPath(string $scope=Scopes::Auto, bool $win32=false): string { - return self::getDataPath($scope, $win32) . DIRECTORY_SEPARATOR . 'tmp'; - } - - /** - * Returns the configuration file - * - * @param string $scope - * @param bool $win32 - * @return string - * @throws InvalidScopeException - */ - public static function getConfigurationFile(string $scope=Scopes::Auto, bool $win32=false): string - { - return self::getDataPath($scope, $win32) . DIRECTORY_SEPARATOR . 'config'; - } - - /** - * Returns an array of all the configuration files the current user can access (For global-cross referencing) - * - * @param bool $win32 - * @return array - * @throws InvalidScopeException - */ - public static function getConfigurationFiles(bool $win32=false): array - { - $results = []; - $results[] = self::getConfigurationFile(Scopes::System, $win32); - - if(!in_array(self::getConfigurationFile(Scopes::User, $win32), $results)) - { - $results[] = self::getConfigurationFile(Scopes::User, $win32); - } - - return $results; + return self::getDataPath($scope, $win32) . DIRECTORY_SEPARATOR . 'runners'; } /** @@ -235,18 +202,4 @@ { return self::getDataPath($scope, $win32) . DIRECTORY_SEPARATOR . 'ext'; } - - /** - * Returns the file path where files for the given extension is stored - * - * @param string $extension_name - * @param string $scope - * @param bool $win32 - * @return string - * @throws InvalidScopeException - */ - public static function getNamedExtensionPath(string $extension_name, string $scope=Scopes::Auto, bool $win32=false): string - { - return self::getExtensionPath($scope, $win32) . DIRECTORY_SEPARATOR . Security::sanitizeFilename($extension_name); - } } \ No newline at end of file diff --git a/src/ncc/Utilities/Resolver.php b/src/ncc/Utilities/Resolver.php index 036248a..4d172a0 100644 --- a/src/ncc/Utilities/Resolver.php +++ b/src/ncc/Utilities/Resolver.php @@ -1,11 +1,21 @@ 4096) + return true; + + return false; + } } \ No newline at end of file diff --git a/tests/package_lock/load_package_lock.php b/tests/package_lock/load_package_lock.php new file mode 100644 index 0000000..258c388 --- /dev/null +++ b/tests/package_lock/load_package_lock.php @@ -0,0 +1,9 @@ +load(); + + var_dump($package_lock_manager->getPackageLock());