- Created complete documentation in docs/ directory - Added PROJECT_OVERVIEW.md with feature highlights and getting started guide - Added ARCHITECTURE.md with system design and technical details - Added SECURITY.md with comprehensive security implementation guide - Added DEVELOPMENT.md with development workflows and best practices - Added DEPLOYMENT.md with production deployment instructions - Added API.md with complete REST API documentation - Added CONTRIBUTING.md with contribution guidelines - Added CHANGELOG.md with version history and migration notes - Reorganized all documentation files into docs/ directory for better organization - Updated README.md with proper documentation links and quick navigation - Enhanced project structure with professional documentation standards
579 lines
13 KiB
Markdown
579 lines
13 KiB
Markdown
# Development Guide
|
|
|
|
This guide covers development workflows, coding standards, and best practices for EasyStream development.
|
|
|
|
## Development Environment Setup
|
|
|
|
### Local Development with Docker
|
|
```bash
|
|
# Clone the repository
|
|
git clone <repository-url>
|
|
cd easystream
|
|
|
|
# Copy environment configuration
|
|
cp .env.example .env
|
|
|
|
# Start development environment
|
|
docker-compose up -d --build
|
|
|
|
# Access the application
|
|
open http://localhost:8083
|
|
```
|
|
|
|
### Native PHP Development
|
|
```bash
|
|
# Install PHP 8.2+ with required extensions
|
|
# - pdo_mysql
|
|
# - gd
|
|
# - curl
|
|
# - mbstring
|
|
# - xml
|
|
# - zip
|
|
|
|
# Install Composer dependencies (if any)
|
|
composer install
|
|
|
|
# Configure database connection
|
|
cp f_core/config.database.php.example f_core/config.database.php
|
|
# Edit database settings
|
|
|
|
# Set up web server (Apache/Nginx)
|
|
# Point document root to project directory
|
|
```
|
|
|
|
## Project Structure Deep Dive
|
|
|
|
### Core Framework (`f_core/`)
|
|
```
|
|
f_core/
|
|
├── config.*.php # Configuration files
|
|
├── f_classes/ # Core business logic classes
|
|
├── f_functions/ # Utility functions
|
|
└── f_workers/ # Background workers
|
|
```
|
|
|
|
### Modules (`f_modules/`)
|
|
```
|
|
f_modules/
|
|
├── m_frontend/ # User-facing modules
|
|
├── m_backend/ # Admin modules
|
|
└── api/ # API endpoints
|
|
```
|
|
|
|
### Templates (`f_templates/`)
|
|
```
|
|
f_templates/
|
|
├── tpl_frontend/ # User interface templates
|
|
├── tpl_backend/ # Admin interface templates
|
|
└── frontend/ # Static frontend assets
|
|
```
|
|
|
|
### Jobs (`f_jobs/`)
|
|
```
|
|
f_jobs/
|
|
├── BaseJob.php # Base job class
|
|
├── VideoProcessingJob.php
|
|
├── NotificationJob.php
|
|
└── CleanupJob.php
|
|
```
|
|
|
|
## Coding Standards
|
|
|
|
### PHP Standards
|
|
Follow PSR-12 coding standards with EasyStream-specific conventions:
|
|
|
|
```php
|
|
<?php
|
|
/**
|
|
* Class description
|
|
*
|
|
* @author Your Name
|
|
* @version 1.0
|
|
*/
|
|
class VExampleClass
|
|
{
|
|
/**
|
|
* Method description
|
|
*
|
|
* @param string $param Parameter description
|
|
* @return bool Return value description
|
|
*/
|
|
public function exampleMethod(string $param): bool
|
|
{
|
|
// Implementation
|
|
return true;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Naming Conventions
|
|
- Classes: PascalCase with 'V' prefix for core classes (`VDatabase`, `VSecurity`)
|
|
- Methods: camelCase (`getUserById`, `validateInput`)
|
|
- Variables: camelCase (`$userId`, `$videoData`)
|
|
- Constants: UPPER_SNAKE_CASE (`MAX_FILE_SIZE`, `DEFAULT_TIMEOUT`)
|
|
- Files: lowercase with underscores (`class.database.php`, `functions.security.php`)
|
|
|
|
### Database Conventions
|
|
- Tables: lowercase with underscores (`user_sessions`, `video_metadata`)
|
|
- Columns: lowercase with underscores (`user_id`, `created_at`)
|
|
- Primary keys: `id` (auto-increment)
|
|
- Foreign keys: `{table}_id` (`user_id`, `video_id`)
|
|
- Timestamps: `created_at`, `updated_at`
|
|
|
|
## Development Workflows
|
|
|
|
### Feature Development
|
|
1. **Create Feature Branch**
|
|
```bash
|
|
git checkout -b feature/new-feature-name
|
|
```
|
|
|
|
2. **Create Spec (Optional)**
|
|
```bash
|
|
# For complex features, create a spec
|
|
mkdir -p .kiro/specs/feature-name
|
|
# Create requirements.md, design.md, tasks.md
|
|
```
|
|
|
|
3. **Implement Feature**
|
|
- Follow the task list if using specs
|
|
- Write tests alongside implementation
|
|
- Update documentation
|
|
|
|
4. **Test Implementation**
|
|
```bash
|
|
# Run all tests
|
|
./run-tests.sh
|
|
|
|
# Run specific test suite
|
|
phpunit tests/Unit/NewFeatureTest.php
|
|
```
|
|
|
|
5. **Create Pull Request**
|
|
- Ensure all tests pass
|
|
- Update CHANGELOG.md
|
|
- Request code review
|
|
|
|
### Bug Fix Workflow
|
|
1. **Reproduce Issue**
|
|
- Create test case that demonstrates the bug
|
|
- Document expected vs actual behavior
|
|
|
|
2. **Fix Implementation**
|
|
- Make minimal changes to fix the issue
|
|
- Ensure fix doesn't break existing functionality
|
|
|
|
3. **Verify Fix**
|
|
- Run test suite
|
|
- Manual testing of affected areas
|
|
- Performance impact assessment
|
|
|
|
## Testing Guidelines
|
|
|
|
### Unit Testing
|
|
```php
|
|
<?php
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class VSecurityTest extends TestCase
|
|
{
|
|
public function testValidateInput()
|
|
{
|
|
$result = VSecurity::validateInput('test@example.com', 'email');
|
|
$this->assertEquals('test@example.com', $result);
|
|
}
|
|
|
|
public function testValidateInputInvalid()
|
|
{
|
|
$this->expectException(ValidationException::class);
|
|
VSecurity::validateInput('invalid-email', 'email');
|
|
}
|
|
}
|
|
```
|
|
|
|
### Integration Testing
|
|
```php
|
|
<?php
|
|
class AuthIntegrationTest extends TestCase
|
|
{
|
|
public function testUserLoginFlow()
|
|
{
|
|
// Test complete login process
|
|
$user = $this->createTestUser();
|
|
$result = VAuth::authenticate($user['username'], 'password');
|
|
$this->assertTrue($result);
|
|
$this->assertTrue(VAuth::isLoggedIn());
|
|
}
|
|
}
|
|
```
|
|
|
|
### Test Data Management
|
|
```php
|
|
<?php
|
|
// tests/fixtures/test_data.sql
|
|
INSERT INTO users (username, email, password_hash) VALUES
|
|
('testuser', 'test@example.com', '$2y$10$...');
|
|
|
|
// In test classes
|
|
protected function setUp(): void
|
|
{
|
|
$this->loadFixtures('test_data.sql');
|
|
}
|
|
```
|
|
|
|
## Database Development
|
|
|
|
### Migration Pattern
|
|
```php
|
|
<?php
|
|
// __install/updatedb_feature.sql
|
|
-- Add new table
|
|
CREATE TABLE IF NOT EXISTS feature_data (
|
|
id INT AUTO_INCREMENT PRIMARY KEY,
|
|
user_id INT NOT NULL,
|
|
data TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
FOREIGN KEY (user_id) REFERENCES users(id)
|
|
);
|
|
|
|
-- Add index
|
|
CREATE INDEX idx_feature_user ON feature_data(user_id);
|
|
```
|
|
|
|
### Query Development
|
|
```php
|
|
<?php
|
|
// Always use prepared statements
|
|
class VFeatureRepository
|
|
{
|
|
public function getFeatureData(int $userId): array
|
|
{
|
|
global $class_database;
|
|
|
|
$sql = "SELECT * FROM feature_data WHERE user_id = ? ORDER BY created_at DESC";
|
|
return $class_database->execute($sql, [$userId])->getRows();
|
|
}
|
|
|
|
public function createFeatureData(int $userId, string $data): bool
|
|
{
|
|
global $class_database;
|
|
|
|
$sql = "INSERT INTO feature_data (user_id, data) VALUES (?, ?)";
|
|
return $class_database->execute($sql, [$userId, $data])->rowCount() > 0;
|
|
}
|
|
}
|
|
```
|
|
|
|
## Frontend Development
|
|
|
|
### Template Development
|
|
```smarty
|
|
{* f_templates/tpl_frontend/feature.tpl *}
|
|
<div class="feature-container">
|
|
<h2>{$feature_title|escape}</h2>
|
|
|
|
{if $user_logged_in}
|
|
<form method="post" action="{$form_action}">
|
|
{csrf_field('feature_form')}
|
|
<input type="text" name="feature_input" value="{$feature_value|escape}">
|
|
<button type="submit">Submit</button>
|
|
</form>
|
|
{else}
|
|
<p>Please <a href="/login">login</a> to use this feature.</p>
|
|
{/if}
|
|
</div>
|
|
```
|
|
|
|
### JavaScript Development
|
|
```javascript
|
|
// f_templates/frontend/js/feature.js
|
|
class FeatureManager {
|
|
constructor() {
|
|
this.initializeEventListeners();
|
|
}
|
|
|
|
initializeEventListeners() {
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
this.setupFeatureForm();
|
|
});
|
|
}
|
|
|
|
setupFeatureForm() {
|
|
const form = document.getElementById('feature-form');
|
|
if (form) {
|
|
form.addEventListener('submit', this.handleFormSubmit.bind(this));
|
|
}
|
|
}
|
|
|
|
async handleFormSubmit(event) {
|
|
event.preventDefault();
|
|
|
|
const formData = new FormData(event.target);
|
|
|
|
try {
|
|
const response = await fetch('/api/feature', {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json();
|
|
this.handleResponse(result);
|
|
} catch (error) {
|
|
console.error('Feature submission failed:', error);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize when DOM is ready
|
|
new FeatureManager();
|
|
```
|
|
|
|
## API Development
|
|
|
|
### RESTful API Structure
|
|
```php
|
|
<?php
|
|
// api/feature.php
|
|
require_once '../f_core/config.core.php';
|
|
|
|
class FeatureAPI
|
|
{
|
|
public function handleRequest()
|
|
{
|
|
$method = $_SERVER['REQUEST_METHOD'];
|
|
|
|
switch ($method) {
|
|
case 'GET':
|
|
return $this->getFeature();
|
|
case 'POST':
|
|
return $this->createFeature();
|
|
case 'PUT':
|
|
return $this->updateFeature();
|
|
case 'DELETE':
|
|
return $this->deleteFeature();
|
|
default:
|
|
http_response_code(405);
|
|
return ['error' => 'Method not allowed'];
|
|
}
|
|
}
|
|
|
|
private function getFeature()
|
|
{
|
|
$id = get_param('id', 'int');
|
|
|
|
if (!$id) {
|
|
http_response_code(400);
|
|
return ['error' => 'Feature ID required'];
|
|
}
|
|
|
|
// Implementation
|
|
return ['feature' => $featureData];
|
|
}
|
|
}
|
|
|
|
// Handle the request
|
|
$api = new FeatureAPI();
|
|
header('Content-Type: application/json');
|
|
echo json_encode($api->handleRequest());
|
|
```
|
|
|
|
## Background Jobs
|
|
|
|
### Job Implementation
|
|
```php
|
|
<?php
|
|
// f_jobs/FeatureProcessingJob.php
|
|
class FeatureProcessingJob extends BaseJob
|
|
{
|
|
protected string $jobType = 'feature_processing';
|
|
|
|
public function execute(array $data): bool
|
|
{
|
|
try {
|
|
$featureId = $data['feature_id'] ?? null;
|
|
|
|
if (!$featureId) {
|
|
throw new InvalidArgumentException('Feature ID required');
|
|
}
|
|
|
|
$this->processFeature($featureId);
|
|
|
|
return true;
|
|
} catch (Exception $e) {
|
|
$this->logError('Feature processing failed', [
|
|
'error' => $e->getMessage(),
|
|
'data' => $data
|
|
]);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private function processFeature(int $featureId): void
|
|
{
|
|
// Implementation
|
|
}
|
|
}
|
|
```
|
|
|
|
### Job Queue Management
|
|
```php
|
|
<?php
|
|
// Queue a job
|
|
$queueManager = new VQueueManager();
|
|
$queueManager->addJob('feature_processing', [
|
|
'feature_id' => $featureId,
|
|
'priority' => 'high'
|
|
]);
|
|
|
|
// Process jobs (in worker)
|
|
$worker = new QueueWorker();
|
|
$worker->processJobs();
|
|
```
|
|
|
|
## Security Development
|
|
|
|
### Input Validation
|
|
```php
|
|
<?php
|
|
// Always validate input
|
|
$username = post_param('username', 'alphanum');
|
|
$email = post_param('email', 'email');
|
|
$age = post_param('age', 'int');
|
|
|
|
// Custom validation
|
|
$customValue = VSecurity::validateInput($input, [
|
|
'type' => 'custom',
|
|
'pattern' => '/^[A-Z]{2,5}$/',
|
|
'required' => true
|
|
]);
|
|
```
|
|
|
|
### CSRF Protection
|
|
```php
|
|
<?php
|
|
// In forms
|
|
echo csrf_field('form_action');
|
|
|
|
// Validation
|
|
if (!validate_csrf('form_action')) {
|
|
throw new SecurityException('CSRF validation failed');
|
|
}
|
|
```
|
|
|
|
### Rate Limiting
|
|
```php
|
|
<?php
|
|
// Check rate limit
|
|
if (!check_rate_limit('api_' . $userId, 100, 3600)) {
|
|
http_response_code(429);
|
|
exit('Rate limit exceeded');
|
|
}
|
|
```
|
|
|
|
## Performance Optimization
|
|
|
|
### Database Optimization
|
|
- Use appropriate indexes
|
|
- Optimize query structure
|
|
- Implement query caching
|
|
- Use connection pooling
|
|
|
|
### Caching Strategy
|
|
```php
|
|
<?php
|
|
// Application-level caching
|
|
$cacheKey = "user_data_{$userId}";
|
|
$userData = $cache->get($cacheKey);
|
|
|
|
if (!$userData) {
|
|
$userData = $this->loadUserData($userId);
|
|
$cache->set($cacheKey, $userData, 3600); // 1 hour
|
|
}
|
|
```
|
|
|
|
### Template Optimization
|
|
- Enable Smarty template caching
|
|
- Minimize template complexity
|
|
- Use template inheritance
|
|
- Optimize asset loading
|
|
|
|
## Debugging and Troubleshooting
|
|
|
|
### Debug Mode
|
|
```php
|
|
<?php
|
|
// Enable debug mode in development
|
|
$cfg['debug_mode'] = true;
|
|
|
|
// Debug logging
|
|
VLogger::debug('Debug information', [
|
|
'variable' => $value,
|
|
'context' => $context
|
|
]);
|
|
```
|
|
|
|
### Error Handling
|
|
```php
|
|
<?php
|
|
try {
|
|
// Risky operation
|
|
$result = $this->performOperation();
|
|
} catch (Exception $e) {
|
|
VLogger::error('Operation failed', [
|
|
'error' => $e->getMessage(),
|
|
'trace' => $e->getTraceAsString()
|
|
]);
|
|
|
|
// Handle gracefully
|
|
return $this->handleError($e);
|
|
}
|
|
```
|
|
|
|
### Log Analysis
|
|
```bash
|
|
# View recent errors
|
|
tail -f f_data/logs/error_$(date +%Y-%m-%d).log
|
|
|
|
# Search for specific issues
|
|
grep "CRITICAL" f_data/logs/*.log
|
|
|
|
# Monitor performance
|
|
grep "SLOW_QUERY" f_data/logs/*.log
|
|
```
|
|
|
|
## Deployment Preparation
|
|
|
|
### Pre-deployment Checklist
|
|
- [ ] All tests passing
|
|
- [ ] Security review completed
|
|
- [ ] Performance testing done
|
|
- [ ] Database migrations ready
|
|
- [ ] Configuration updated
|
|
- [ ] Documentation updated
|
|
|
|
### Production Considerations
|
|
- Disable debug mode
|
|
- Configure proper logging levels
|
|
- Set up monitoring and alerts
|
|
- Implement backup procedures
|
|
- Configure SSL/TLS properly
|
|
|
|
## Code Review Guidelines
|
|
|
|
### Review Checklist
|
|
- [ ] Code follows project standards
|
|
- [ ] Security best practices implemented
|
|
- [ ] Tests cover new functionality
|
|
- [ ] Documentation updated
|
|
- [ ] Performance impact considered
|
|
- [ ] Error handling implemented
|
|
- [ ] Logging added where appropriate
|
|
|
|
### Common Issues to Check
|
|
- SQL injection vulnerabilities
|
|
- XSS vulnerabilities
|
|
- Missing input validation
|
|
- Improper error handling
|
|
- Performance bottlenecks
|
|
- Missing tests
|
|
- Inadequate logging |