An open API service indexing awesome lists of open source software.

https://github.com/aiptu/libplaceholder

A flexible placeholder library for PocketMine-MP plugins, allowing dynamic insertion of player and global data into messages.
https://github.com/aiptu/libplaceholder

minecraft php8 placeholderapi pocketmine-mp

Last synced: 4 months ago
JSON representation

A flexible placeholder library for PocketMine-MP plugins, allowing dynamic insertion of player and global data into messages.

Awesome Lists containing this project

README

          

# 🎯 libplaceholder

[![PHP Version](https://img.shields.io/badge/php-%5E8.3-blue)](https://www.php.net/)
[![PocketMine-MP](https://img.shields.io/badge/PocketMine--MP-%5E5.36.0-green)](https://github.com/pmmp/PocketMine-MP)
[![License](https://img.shields.io/badge/license-MIT-lightgrey)](LICENSE.md)

A flexible placeholder library for PocketMine-MP plugins, allowing dynamic insertion of player and global data into messages.

## ✨ Features

- **πŸš€ 3.75x Faster**: Optimized regex caching, fast-path checks, and match expressions
- **πŸ’Ž Immutable Context**: Thread-safe design prevents accidental state mutations
- **🎨 Color Support**: Automatic TextFormat colorization with opt-out
- **πŸ“¦ Extensible**: Register custom placeholder handlers with namespaced groups
- **πŸ›‘οΈ Production-Ready**: Input validation, exception handling, comprehensive tests
- **⚑ Zero-Allocation Parsing**: Minimal memory overhead per parse operation

## πŸ“Š Performance

```
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Scenario β”‚ Before β”‚ After β”‚ Improvement β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Parse 10 placeholders β”‚ 45Β΅s β”‚ 12Β΅s β”‚ 3.75x β”‚
β”‚ Parse plain text β”‚ 5Β΅s β”‚ 2Β΅s β”‚ 2.5x β”‚
β”‚ Memory (1000 parses) β”‚ 2.4MB β”‚ 2.1MB β”‚ -12% β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
```

## πŸ“¦ Installation

### Composer (Recommended)
```bash
composer require aiptu/libplaceholder
```

## πŸš€ Quick Start

### Basic Usage

```php
use aiptu\libplaceholder\PlaceholderManager;
use aiptu\libplaceholder\PlaceholderContext;

// Initialize once in onEnable()
PlaceholderManager::getInstance()->init();

// Create context with player
$context = new PlaceholderContext($player);

// Parse message
$message = "Hello {player:name}! Health: {player:health}/{player:max_health}";
$parsed = PlaceholderManager::getInstance()->parsePlaceholders($message, $context);
// Result: "Hello Steve! Health: 18.50/20"
```

### Custom Data Context

```php
$context = new PlaceholderContext($player);
$context = $context->withData('guild', 'Warriors')
->withData('rank', 'Leader')
->withData('coins', 1500);

$message = "Welcome {player:name} of {guild}!{line}Rank: {rank}{line}Coins: {coins}";
$parsed = PlaceholderManager::getInstance()->parsePlaceholders($message, $context);
```

### Performance Tip: Raw Parsing

```php
// Skip TextFormat colorization for better performance
$parsed = PlaceholderManager::getInstance()->parsePlaceholdersRaw($message, $context);
```

## πŸ”§ Creating Custom Handlers

```php
use aiptu\libplaceholder\PlaceholderHandler;
use aiptu\libplaceholder\PlaceholderContext;

final class EconomyPlaceholderHandler implements PlaceholderHandler {
public function __construct(
private EconomyAPI $economy
) {}

public function handle(string $placeholder, PlaceholderContext $context, string ...$args): string {
$player = $context->getPlayer();
if ($player === null) {
return 'N/A';
}

return match($placeholder) {
'money' => number_format($this->economy->myMoney($player), 2),
'currency' => $this->economy->getCurrencySymbol(),
'rank' => $this->economy->getRank($player),
'top' => $this->getTopPlayer((int) ($args[0] ?? 1)),
default => '{' . $placeholder . '}'
};
}

private function getTopPlayer(int $position): string {
// Your implementation
return "Player#{$position}";
}
}

// Register the handler
PlaceholderManager::getInstance()->registerHandler(
'economy',
new EconomyPlaceholderHandler(EconomyAPI::getInstance())
);

// Usage
$msg = "Balance: {economy:currency}{economy:money}{line}Top Player: {economy:top:1}";
```

## πŸ“– Placeholder Syntax

```
{group:placeholder:arg1,arg2|fallback}
```

**Components:**
- `group` - Handler namespace (e.g., `player`, `economy`)
- `placeholder` - Specific value identifier (e.g., `name`, `health`)
- `args` - Comma-separated arguments (optional)
- `fallback` - Default value if resolution fails (optional)

**Special Placeholders:**
- `{line}` - Inserts newline character (`\n`)

**Examples:**
```php
"{player:name}" // Simple placeholder
"{player:health:2}" // With precision argument
"{economy:money|0.00}" // With fallback
"{guild:name|No Guild}" // Fallback for missing data
"{player:x}, {player:y}, {player:z}" // Multiple placeholders
```

## πŸ“‹ Built-in Player Placeholders

### Basic Information
| Placeholder | Description | Example |
|-------------|-------------|---------|
| `{player:name}` | Username | `Steve` |
| `{player:display_name}` | Display name with formatting | `Β§aSteve` |
| `{player:ip}` | IP address | `127.0.0.1` |
| `{player:port}` | Connection port | `19132` |
| `{player:ping}` | Network latency (ms) | `45` |

### Health & Food
| Placeholder | Description | Example |
|-------------|-------------|---------|
| `{player:health}` | Current health (2 decimals) | `18.50` |
| `{player:max_health}` | Maximum health | `20` |
| `{player:health_percentage}` | Health as percentage | `92.5` |
| `{player:food}` | Food level | `18` |
| `{player:max_food}` | Maximum food level | `20` |
| `{player:saturation}` | Saturation level | `5.60` |

### Position & World
| Placeholder | Description | Example |
|-------------|-------------|---------|
| `{player:x}` | X coordinate (floor) | `128` |
| `{player:y}` | Y coordinate (floor) | `64` |
| `{player:z}` | Z coordinate (floor) | `-45` |
| `{player:world}` | World display name | `Lobby` |
| `{player:world_folder}` | World folder name | `worlds/lobby` |

### Experience & Gamemode
| Placeholder | Description | Example |
|-------------|-------------|---------|
| `{player:xp_level}` | Experience level | `30` |
| `{player:xp_progress}` | XP progress to next level (%) | `45.2` |
| `{player:gamemode}` | Gamemode name | `Survival` |
| `{player:gamemode_id}` | Gamemode ID | `0` |

## πŸ† Acknowledgments

Built with ❀️ by **AIPTU**

---

**Star ⭐ this repository if you find it useful!**