MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / detect_factory_pattern

Function detect_factory_pattern

src/code_actions/extract_function.rs:507–586  ·  view source on GitHub ↗

Detect if the body text is a factory pattern: the extracted code constructs an object and returns it. Returns a name suitable for `create{Name}`. When the body assigns `$var = new X(…)` and later returns `$var`, the variable name is used (e.g. `$users` → `"Users"`). This produces `createUsers` rather than `createCollection`, which matches how developers think about the domain object. When the

(body: &str)

Source from the content-addressed store, hash-verified

505/// When the body does `return new ClassName(…)` directly, the class
506/// name is used instead (there is no variable to take a hint from).
507fn detect_factory_pattern(body: &str) -> Option<String> {
508 let mut returned_class: Option<String> = None;
509 let mut returned_var: Option<String> = None;
510 let mut assigned_var: Option<String> = None;
511 let mut assigned_class: Option<String> = None;
512
513 for line in body.lines() {
514 let trimmed = line.trim();
515 // Check for `return new ClassName(…)` — direct return.
516 if let Some(after_return) = trimmed.strip_prefix("return ")
517 && let Some(name) = extract_new_class_name(after_return.trim_start())
518 {
519 returned_class = Some(name);
520 }
521 // Check for `return $var;` — returning a variable.
522 if let Some(after_return) = trimmed.strip_prefix("return ") {
523 let var = after_return.trim().trim_end_matches(';').trim();
524 if var.starts_with('$') && var[1..].chars().all(|c| c.is_alphanumeric() || c == '_') {
525 returned_var = Some(var.to_string());
526 }
527 }
528 // Check for `$var = new ClassName(…)` (direct assignment).
529 if let Some(eq_pos) = trimmed.find('=') {
530 // Make sure it's `=` not `==` / `===` / `!=` etc.
531 let before_eq = &trimmed[..eq_pos];
532 let after_eq = &trimmed[eq_pos + 1..];
533 let var_name = before_eq.trim();
534 if var_name.starts_with('$')
535 && !after_eq.starts_with('=')
536 && !before_eq.ends_with('!')
537 && !before_eq.ends_with('<')
538 && !before_eq.ends_with('>')
539 && let Some(class_name) = extract_new_class_name(after_eq.trim_start())
540 {
541 assigned_var = Some(var_name.to_string());
542 assigned_class = Some(class_name);
543 }
544 }
545 }
546
547 // Best case: `$var = new X(…); ... return $var;` — use the
548 // variable name because it carries domain meaning (e.g. `$users`
549 // → `createUsers`). Fall back to the class name when the variable
550 // is too short to be meaningful (`$u`, `$x`, etc.).
551 if let Some(ref ret_var) = returned_var
552 && let Some(ref asgn_var) = assigned_var
553 && ret_var == asgn_var
554 {
555 let var_clean = ret_var.trim_start_matches('$');
556 if var_clean.len() > 2 {
557 return Some(capitalise(var_clean));
558 }
559 // Short variable — prefer the class name.
560 if let Some(ref name) = assigned_class {
561 let short = name.rsplit('\\').next().unwrap_or(name);
562 return Some(short.to_string());
563 }
564 }

Callers 1

derive_base_nameFunction · 0.85

Calls 5

extract_new_class_nameFunction · 0.85
capitaliseFunction · 0.85
nextMethod · 0.80
allMethod · 0.45
findMethod · 0.45

Tested by

no test coverage detected