Skip to main content

source_map_php/adapters/
mod.rs

1mod hyperf;
2mod laravel;
3
4use std::fs;
5use std::path::Path;
6
7use anyhow::Result;
8use regex::Regex;
9
10use crate::Framework;
11use crate::models::{RouteDoc, SchemaDoc, make_stable_id};
12use crate::sanitizer::Sanitizer;
13
14pub fn detect_framework(repo: &Path, requested: Framework, package_names: &[String]) -> Framework {
15    if requested != Framework::Auto {
16        return requested;
17    }
18    if package_names.iter().any(|name| name == "hyperf/hyperf")
19        || repo.join("bin/hyperf.php").exists()
20    {
21        Framework::Hyperf
22    } else if package_names.iter().any(|name| name == "laravel/framework")
23        || repo.join("artisan").exists()
24    {
25        Framework::Laravel
26    } else {
27        Framework::Auto
28    }
29}
30
31pub fn extract_routes(
32    repo: &Path,
33    repo_name: &str,
34    framework: Framework,
35    sanitizer: &Sanitizer,
36) -> Result<Vec<RouteDoc>> {
37    match framework {
38        Framework::Laravel => laravel::extract_routes(repo, repo_name, sanitizer),
39        Framework::Hyperf => hyperf::extract_routes(repo, repo_name, sanitizer),
40        Framework::Auto => Ok(Vec::new()),
41    }
42}
43
44pub fn extract_schema(repo: &Path, repo_name: &str) -> Result<Vec<SchemaDoc>> {
45    let migration_roots = [repo.join("database/migrations"), repo.join("migrations")];
46    let create_re = Regex::new(r#"Schema::create\(\s*['"]([^'"]+)['"]"#).unwrap();
47    let table_re = Regex::new(r#"Schema::table\(\s*['"]([^'"]+)['"]"#).unwrap();
48    let mut docs = Vec::new();
49    for migrations in migration_roots {
50        if !migrations.exists() {
51            continue;
52        }
53        for entry in walkdir::WalkDir::new(&migrations)
54            .into_iter()
55            .filter_map(Result::ok)
56            .filter(|entry| entry.file_type().is_file())
57            .filter(|entry| entry.path().extension().and_then(|ext| ext.to_str()) == Some("php"))
58        {
59            let contents = fs::read_to_string(entry.path())?;
60            for (idx, line) in contents.lines().enumerate() {
61                let capture = create_re.captures(line).or_else(|| table_re.captures(line));
62                if let Some(capture) = capture {
63                    let table = capture.get(1).unwrap().as_str().to_string();
64                    let operation = if line.contains("Schema::create") {
65                        "create"
66                    } else {
67                        "table"
68                    };
69                    let path = entry
70                        .path()
71                        .strip_prefix(repo)
72                        .unwrap()
73                        .to_string_lossy()
74                        .into_owned();
75                    docs.push(SchemaDoc {
76                        id: make_stable_id(&[repo_name, &path, &table, operation]),
77                        repo: repo_name.to_string(),
78                        migration: entry.file_name().to_string_lossy().into_owned(),
79                        table: Some(table),
80                        operation: operation.to_string(),
81                        path,
82                        line_start: idx + 1,
83                    });
84                }
85            }
86        }
87    }
88    Ok(docs)
89}