Skip to content

GeneaLabs/zed-phpmd-lsp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

PHPMD Language Server for Zed Editor

MIT License PHP Zed PHPMD Rust

Real-time PHP code quality analysis powered by PHP Mess Detector

Features β€’ Installation β€’ Configuration β€’ Architecture β€’ Contributing


πŸ“‹ Overview

The PHPMD Language Server brings comprehensive code quality analysis to Zed Editor through the Language Server Protocol (LSP). It provides real-time feedback on potential bugs, suboptimal code, overcomplicated expressions, and unused code elements as you write PHP.

Why PHPMD LSP?

  • πŸš€ Instant Feedback - See code quality issues as you type, not after you commit
  • ⚑ High Performance - Optimized with async processing, LZ4 compression, and smart caching
  • 🎯 Zero Configuration - Works out of the box with sensible defaults
  • πŸ”§ Highly Configurable - Customize rules to match your team's standards
  • πŸ“¦ Self-Contained - Includes bundled PHPMD binary, no external dependencies required

✨ Features

Core Capabilities

πŸ” Real-Time Code Analysis

  • Detects code quality issues instantly as you type
  • Smart diagnostic ranges that skip leading whitespace for cleaner underlines
  • Severity-based highlighting (errors, warnings, info)
  • Rule-specific documentation links

⚑ Performance Optimizations

  • LZ4 Compression - Reduces memory usage by ~85% for large files
  • Smart Caching - Avoids re-analyzing unchanged files
  • Concurrent Processing - Analyzes up to 4 files simultaneously
  • Async Architecture - Non-blocking execution keeps editor responsive
  • Timeout Protection - 10-second safeguard prevents hanging on complex files

🎨 Intelligent Diagnostics

  • Context-Aware Ranges - Different highlighting for class, method, and property issues
  • Whitespace Trimming - Underlines start at first code character, not indentation
  • Property Detection - Correctly highlights property violations even when PHPMD reports class-level
  • Rule Categorization - Automatic severity mapping based on rule priority

Configuration Features

πŸ”§ Flexible Configuration

  • Auto-Discovery - Finds phpmd.xml, phpmd.xml.dist, .phpmd.xml, or .phpmd.xml.dist automatically
  • XML Validation - Validates config files before use, skipping invalid XML files
  • Smart Fallback - Uses all PHPMD rulesets when no valid config file is found
  • Live Reloading - Configuration changes apply instantly without restart
  • Multi-Source - Configure via project files, Zed settings, or environment variables
  • Auto-Recovery - Gracefully handles deleted or invalid config files

πŸ“¦ Smart PHPMD Detection

Priority order for finding PHPMD executable:

  1. Project's vendor/bin/phpmd (respects composer dependencies)
  2. Custom path from settings
  3. Bundled PHPMD 2.15+ PHAR (always available fallback)
  4. System PATH installation

Reliability Features

πŸ›‘οΈ Robust Error Handling

  • Process Management - Automatic cleanup of zombie processes
  • Memory Monitoring - Tracks and logs memory usage for optimization
  • Graceful Degradation - Falls back to defaults when configs fail
  • Comprehensive Logging - Detailed debug output for troubleshooting

πŸ“¦ Installation

Via Zed Extensions Panel (Recommended)

  1. Open Zed Editor
  2. Press Cmd+Shift+X (macOS) or Ctrl+Shift+X (Linux/Windows)
  3. Search for "PHPMD"
  4. Click Install

Manual Installation (Development)

# Clone the repository
git clone https://github.com/GeneaLabs/zed-phpmd-lsp.git
cd zed-phpmd-lsp

# Build the extension
cargo build --release

# Build the LSP server
cd lsp-server
cargo build --release

πŸš€ Quick Start

1. Enable the Language Server

Add to your Zed settings.json (open with Cmd+, or Ctrl+,):

{
  "languages": {
    "PHP": {
      "language_servers": ["phpmd", "intelephense"]
    }
  }
}

2. Start Coding

The extension automatically analyzes PHP files as you open and edit them:

<?php
namespace App\Services;

class UserService
{
    private $unused; // πŸ” Unused private field
    
    public function complexMethod($a, $b, $c, $d, $e, $f) // πŸ” Too many parameters
    {
        if ($a == true) { // πŸ” Use strict comparison
            goto end; // πŸ” Avoid goto statements
        }
        
        eval($userInput); // πŸ” Security risk: eval usage
        
        end:
        return $result;
    }
    
    public function m() { // πŸ” Method name too short
        // Complex logic here...
    }
}

βš™οΈ Configuration

Configuration Priority

The extension uses this priority order (highest to lowest):

  1. Project Config Files - phpmd.xml, phpmd.xml.dist, .phpmd.xml
  2. Zed Settings - User or project-specific settings.json
  3. Environment Variables - PHPMD_RULESETS, PHPMD_PATH
  4. Defaults - Built-in rulesets: cleancode, codesize, controversial, design, naming, unusedcode

Available Rulesets

Ruleset Description Key Rules
cleancode Clean code practices BooleanArgumentFlag, ElseExpression, StaticAccess
codesize Complexity and size limits CyclomaticComplexity, NPathComplexity, ExcessiveMethodLength
controversial Debated best practices Superglobals, CamelCaseParameterName
design Software design principles ExitExpression, EvalExpression, GotoStatement
naming Naming conventions ShortVariable, LongVariable, ShortMethodName
unusedcode Dead code detection UnusedPrivateField, UnusedLocalVariable, UnusedPrivateMethod

Configuration Examples

Basic Zed Settings

{
  "lsp": {
    "phpmd": {
      "settings": {
        "rulesets": "cleancode,codesize,design"
      }
    }
  }
}

Advanced Project Configuration

Create phpmd.xml in your project root:

<?xml version="1.0"?>
<ruleset name="MyProject"
         xmlns="http://pmd.sf.net/ruleset/1.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://pmd.sf.net/ruleset/1.0.0
                             http://pmd.sf.net/ruleset_xml_schema.xsd">
    
    <description>Custom rules for MyProject</description>
    
    <!-- Import specific rules -->
    <rule ref="rulesets/cleancode.xml">
        <exclude name="ElseExpression"/>
    </rule>
    
    <rule ref="rulesets/codesize.xml/CyclomaticComplexity">
        <properties>
            <property name="reportLevel" value="15"/>
        </properties>
    </rule>
    
    <rule ref="rulesets/naming.xml/ShortVariable">
        <properties>
            <property name="minimum" value="2"/>
            <property name="exceptions" value="id,db,em,i,j,k"/>
        </properties>
    </rule>
    
    <!-- Exclude patterns -->
    <exclude-pattern>*/vendor/*</exclude-pattern>
    <exclude-pattern>*/tests/*</exclude-pattern>
    <exclude-pattern>*/database/migrations/*</exclude-pattern>
</ruleset>

Per-Project Settings

Create .zed/settings.json in your project:

{
  "lsp": {
    "phpmd": {
      "settings": {
        "rulesets": "./config/phpmd-strict.xml",
        "phpmdPath": "./vendor/bin/phpmd"
      }
    }
  }
}

πŸ—οΈ Architecture

System Overview

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     LSP Protocol      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                 β”‚ ◄──────────────────► β”‚                  β”‚
β”‚   Zed Editor    β”‚                      β”‚   PHPMD LSP      β”‚
β”‚                 β”‚                      β”‚    Server        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
        β”‚                                         β”‚
        β”‚                                         β”‚
        β–Ό                                         β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  PHP Files      β”‚                      β”‚   PHPMD Binary   β”‚
β”‚  in Workspace   β”‚                      β”‚   (Analysis)     β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Components

LSP Server (lsp-server/src/main.rs)

  • Tower-LSP Framework - Async LSP implementation in Rust
  • Tokio Runtime - High-performance async execution
  • LZ4 Compression - Memory-efficient document storage
  • Semaphore Control - Limits concurrent PHPMD processes

Memory Management

// Document compression reduces memory by ~85%
CompressedDocument {
    compressed_data: Vec<u8>,      // LZ4 compressed content
    original_size: usize,           // Original file size
    checksum: u64,                  // Content hash for cache validation
    compression_ratio: f32,         // Monitoring compression efficiency
}

Diagnostic Processing Pipeline

  1. File Change Detection - Debounced to avoid excessive processing
  2. Content Compression - LZ4 compression for memory efficiency
  3. PHPMD Execution - Isolated process with timeout protection
  4. JSON Parsing - Robust extraction from mixed PHPMD output
  5. Range Calculation - Smart positioning with whitespace trimming
  6. Cache Storage - Results cached with content checksums

Performance Characteristics

  • Memory Usage: ~15-20MB base + ~0.2MB per open file (compressed)
  • Analysis Speed: 50-200ms for typical files (1000 lines)
  • Concurrent Files: Up to 4 simultaneous analyses
  • Cache Hit Rate: ~70-80% in typical editing sessions
  • Compression Ratio: 85-90% size reduction for source files

πŸ› Troubleshooting

Common Issues

Diagnostics Not Appearing

  1. Verify language server is enabled:

    {
      "languages": {
        "PHP": {
          "language_servers": ["phpmd"]
        }
      }
    }
  2. Check debug output:

    • View β†’ Toggle Log Panel
    • Look for PHPMD LSP: prefixed messages
  3. Test PHPMD manually:

    phpmd path/to/file.php text cleancode

Performance Issues

  1. Reduce concurrent analyses:

    {
      "lsp": {
        "phpmd": {
          "settings": {
            "maxConcurrent": 2
          }
        }
      }
    }
  2. Simplify rulesets:

    • Start with single ruleset: "rulesets": "cleancode"
    • Add more gradually to identify performance impact

Custom Rules Not Working

  1. Validate XML syntax:

    xmllint --noout phpmd.xml
  2. Check file paths:

    • Use absolute paths for testing
    • Ensure relative paths are from project root
  3. Test configuration:

    phpmd . text phpmd.xml --verbose

Debug Mode

Enable verbose logging in Zed:

{
  "lsp": {
    "phpmd": {
      "settings": {
        "logLevel": "debug"
      }
    }
  }
}

🀝 Contributing

We welcome contributions! See our Contributing Guide for details.

Development Setup

# Clone and setup
git clone https://github.com/GeneaLabs/zed-phpmd-lsp.git
cd zed-phpmd-lsp

# Install Rust toolchain
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Build everything
cargo build --release
cd lsp-server && cargo build --release

# Run tests
cargo test

Areas for Contribution

  • πŸͺŸ Windows testing and compatibility
  • 🐧 Linux distribution testing
  • πŸ“ Additional ruleset templates
  • 🌍 Internationalization support
  • πŸ§ͺ Test coverage improvements
  • πŸ“š Documentation translations

πŸ“š Resources

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.

Third-Party Licenses

  • PHPMD - BSD-3-Clause License (bundled as PHAR)
  • Rust Dependencies - Various permissive licenses

See THIRD_PARTY_LICENSES.md for complete attribution.

πŸ™ Acknowledgments


Made with ❀️ for the PHP community

Report Bug β€’ Request Feature β€’ Discussions

About

PHPMD linter for Zed editor.

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages