@@ -119,8 +119,50 @@ private function discoverExternal(): void
119119
120120 try {
121121 $ provider = new $ fqcn ();
122- if (method_exists ($ provider , 'register ' )) {
122+ $ didRegister = false ;
123+
124+ // Preferred: provider explicitly implements our interface
125+ if ($ provider instanceof \Tamedevelopers \Support \Capsule \CommandProviderInterface) {
123126 $ provider ->register ($ this );
127+ $ didRegister = true ;
128+ }
129+ // Fallback: detect register() signature and call appropriately
130+ elseif (method_exists ($ provider , 'register ' )) {
131+ try {
132+ $ rm = new \ReflectionMethod ($ provider , 'register ' );
133+ $ paramCount = $ rm ->getNumberOfParameters ();
134+
135+ if ($ paramCount === 0 ) {
136+ // Laravel-like providers often have register() with no args
137+ $ provider ->register ();
138+ $ didRegister = true ;
139+ } else {
140+ // If first param accepts our Artisan (or is untyped/mixed/optional), pass it
141+ $ first = $ rm ->getParameters ()[0 ];
142+ $ canPassArtisan = true ;
143+
144+ if ($ first ->hasType ()) {
145+ $ t = $ first ->getType ();
146+ if ($ t instanceof \ReflectionNamedType) {
147+ $ name = ltrim ($ t ->getName (), '\\' );
148+ $ canPassArtisan = ($ name === 'Tamedevelopers \\Support \\Capsule \\Artisan ' ) || ($ name === 'mixed ' ) || ($ name === 'array ' ) || ($ name === 'object ' );
149+ }
150+ }
151+
152+ if ($ canPassArtisan ) {
153+ $ provider ->register ($ this );
154+ $ didRegister = true ;
155+ } elseif ($ first ->isOptional ()) {
156+ $ provider ->register ();
157+ $ didRegister = true ;
158+ }
159+ }
160+ } catch (\Throwable $ ignored ) {
161+ // If reflection or calling fails, we just skip this provider
162+ }
163+ }
164+
165+ if ($ didRegister ) {
124166 self ::$ registeredProviders [$ fqcn ] = true ;
125167 }
126168 } catch (\Throwable $ e ) {
@@ -131,23 +173,34 @@ private function discoverExternal(): void
131173 }
132174
133175 /**
134- * Resolve the vendor directory path for both dev (package root) and consumer app.
176+ * Resolve the vendor directory path robustly for both:
177+ * - Developing this package as the root project (support/vendor)
178+ * - When installed inside another project (project/vendor)
135179 */
136180 private function resolveVendorPath (): ?string
137181 {
138- // Current file: support/Capsule/Traits/ArtisanDiscovery.php
139- $ packageRoot = \dirname (__DIR__ , 3 ); // .../support
182+ // Start from this file's directory and walk upwards to find:
183+ // - a 'vendor' directory sibling (e.g., support/vendor), or
184+ // - a parent directory named 'vendor' (e.g., project/vendor)
185+ $ dir = __DIR__ ;
186+
187+ for ($ i = 0 ; $ i < 8 ; $ i ++) {
188+ if ($ dir === DIRECTORY_SEPARATOR || $ dir === '/ ' || $ dir === '' ) {
189+ break ;
190+ }
140191
141- // Case 1: developing this package as the root project
142- $ vendor = $ packageRoot . DIRECTORY_SEPARATOR . 'vendor ' ;
143- if (is_dir ($ vendor )) {
144- return $ vendor ;
145- }
192+ // Case A: current directory is the vendor directory
193+ if (\basename ($ dir ) === 'vendor ' && \is_dir ($ dir )) {
194+ return $ dir ;
195+ }
196+
197+ // Case B: there is a vendor directory inside the current directory
198+ $ candidate = $ dir . DIRECTORY_SEPARATOR . 'vendor ' ;
199+ if (\is_dir ($ candidate )) {
200+ return $ candidate ;
201+ }
146202
147- // Case 2: this package is installed as a dependency: project/vendor/tamedevelopers/support/...
148- $ maybeProjectVendor = \dirname ($ packageRoot , 2 ); // .../project/vendor
149- if (is_dir ($ maybeProjectVendor )) {
150- return $ maybeProjectVendor ;
203+ $ dir = \dirname ($ dir );
151204 }
152205
153206 return null ;
0 commit comments