Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **Parent member override completion.** Typing a method name after `function` in a class body (for example `protected function get`) now suggests public and protected methods from parent classes and interfaces that can still be overridden or implemented, inserting a full signature snippet. The same flow suggests parent properties after `$` (for example `protected $tit`) and parent constants after `const`. Snippets insert `#[\Override]` above methods on PHP 8.3+, properties on PHP 8.5+, and constants on PHP 8.6+ (from `composer.json` / `config.platform.php`). Private members and ones already defined on the class are omitted. Contributed by @calebdw.
- **Laravel schema dumps power Eloquent model properties.** PHPantom now scans Laravel database schema dumps from `database/schema` by default, reads `config/database.php` for connection drivers/defaults, and uses the parsed columns to synthesize Eloquent model properties with database types, nullability, and defaults in hover. Schema lookup respects model `$connection`, `$table`, Laravel `Connection`/`Table` attributes, dynamic table/connection overrides, and reloads when schema files or related config change. Contributed by @calebdw.
- **Laravel migration scanning for Eloquent model properties.** PHPantom now parses Laravel migration files to infer database columns when schema dumps are not available or to overlay changes on top of dumps. Migrations are discovered from any non-vendor `database/migrations` directory (including nested modules like `modules/billing/database/migrations`), applied in global filename order, and support named and anonymous migration classes, `$connection` properties, `Schema::connection()` calls, `Blueprint::after()` nested closures, `virtualAs`/`storedAs` generated columns, and custom Blueprint macros registered via the existing macro scanner. Migration scanning is incremental: editing a single migration re-reads only that file and replays the cached plan over the base schema without re-reading other files. Configure with `[laravel.migrations] enabled` and `paths` in `.phpantom.toml`. Contributed by @calebdw.
- **By-reference closure captures update outer variable types for immediately-invoked callables.** A closure passed to a callable parameter can now update the inferred type of variables captured with `use (&$var)` when the callable is considered immediately invoked. This follows PHPStan's defaults: function callable parameters are immediate unless marked with `@param-later-invoked-callable`, while method callable parameters are later-invoked unless marked with `@param-immediately-invoked-callable`. Contributed by @calebdw.
Expand Down
46 changes: 39 additions & 7 deletions src/code_actions/implement_methods.rs
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ fn build_method_stubs(
}

/// Format the parameter list for a method stub.
fn format_params(
pub(crate) fn format_params(
method: &MethodInfo,
use_map: &HashMap<String, String>,
file_namespace: &Option<String>,
Expand All @@ -461,12 +461,16 @@ fn format_params(
for param in &method.parameters {
let mut s = String::new();

// Type hint — prefer native type hint (what appears in PHP source)
// over the docblock-enriched one.
// Type hint — only the native PHP signature. Do not fall back to
// docblock `@param` types: parameter types are contravariant, so
// promoting a docblock type (e.g. `string`) onto an untyped parent
// parameter would illegally narrow the override.
if let Some(ref hint) = param.native_type_hint {
let shortened = shorten_php_type_direct(hint, use_map, file_namespace);
s.push_str(&shortened);
s.push(' ');
if !shortened.is_empty() {
s.push_str(&shortened);
s.push(' ');
}
}

// Variadic and reference markers.
Expand All @@ -477,7 +481,13 @@ fn format_params(
s.push_str("...");
}

s.push_str(&param.name);
// Parser stores names without `$`; virtual/docblock params may
// already include it. Always emit a single leading `$`.
let pname = param.name.as_str();
if !pname.starts_with('$') {
s.push('$');
}
s.push_str(pname);

// Default value.
if let Some(ref default) = param.default_value {
Expand Down Expand Up @@ -513,7 +523,7 @@ fn is_valid_native_hint(ty: &PhpType) -> bool {
}

/// Format the return type hint for a method stub.
fn format_return_type(
pub(crate) fn format_return_type(
method: &MethodInfo,
use_map: &HashMap<String, String>,
file_namespace: &Option<String>,
Expand Down Expand Up @@ -714,6 +724,28 @@ mod tests {
assert_eq!(result, "string $name, int $age = 0");
}

#[test]
fn format_params_adds_dollar_when_name_has_none() {
// Real parsed methods store names without `$`.
let method = MethodInfo {
parameters: vec![ParameterInfo {
name: crate::atom::atom("key"),
is_required: true,
type_hint: Some(PhpType::parse("string")),
native_type_hint: None,
description: None,
default_value: None,
is_variadic: false,
is_reference: false,
closure_this_type: None,
}],
..MethodInfo::virtual_method("getAttribute", None)
};
let result = format_params(&method, &HashMap::new(), &None);
// Docblock-only type must not be promoted (contravariance).
assert_eq!(result, "$key");
}

#[test]
fn format_params_variadic_and_reference() {
let method = MethodInfo {
Expand Down
1 change: 1 addition & 0 deletions src/completion/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,6 @@ pub(crate) mod constant_completion;
pub(crate) mod function_completion;
pub(crate) mod keyword_completion;
pub(crate) mod namespace_completion;
pub(crate) mod override_completion;
pub(crate) mod symbol_ranking;
pub(crate) mod type_hint_completion;
Loading
Loading