定制 openspring/springphp-framework 二次开发

按需修改功能、优化性能、对接业务系统,提供一站式技术支持

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

openspring/springphp-framework

Composer 安装命令:

composer require openspring/springphp-framework

包简介

The SpringPHP framework

README 文档

README

pipeline status coverage report License: MIT PHP 7.4+

A PHP 7.4+ framework inspired by Java Spring, bringing annotation-driven dependency injection, RESTful controllers, ORM, JWT security, and validation to PHP.

Table of Contents

Features

  • Annotation-driven architecture — stereotypes (@Component, @Service, @Configuration), DI (@Autowired, @Value), and REST mappings parsed from PHP docblocks
  • IoC Container — singleton and prototype scoped beans with lazy loading and cyclic dependency detection
  • RESTful routing@GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping with path variables, content negotiation, and automatic parameter binding
  • ORM — typed column mapping, fluent query builder, parameter binding, and state machine entity lifecycle
  • JWT Security — token processing with role-based and group-based access control
  • Validation — constraint-based validation (@NotNull, @NotBlank, @Email, @Min, @Max, @Size, @Regex, @Positive, @Past, @Future, etc.)
  • HTTP layer — request/response abstraction, ResponseEntity, message converters (JSON, HTML), cookies, headers, CORS
  • InterceptorspreHandle / postHandle hooks for cross-cutting concerns
  • Logging — built-in logger with Log4p implementation
  • TestingTestRunner for bootstrapping in TEST profile, Mockito-style mocking with when/thenReturn
  • i18nResourceBundleMessageSource for multi-language message loading

Requirements

  • PHP 7.4 or higher
  • Composer
  • MySQL (primary supported database)

Installation

For existing projects

composer require openspring/springphp-framework

For new projects

We recommend starting from the springphp-sample boilerplate, which provides a ready-to-use project structure with default configurations.

You can also use the springphp-initializer CLI to scaffold new projects, generate the database access layer, request mappings, API documentation and build deliveries.

Quick Start

1. Create the application entry point

<?php

use Openspring\SpringphpFramework\Core\SpringphpApplication;

/**
 * @ComponentScan(baseDirs = ["src/controllers", "src/services"])
 * @PropertySource("config/application.properties")
 */
class Application extends SpringphpApplication
{
    public function __construct()
    {
        parent::__construct(__DIR__, 'config/', 'config/test/');
    }
}

$app = new Application();
$app->execute();

2. Create a REST controller

<?php

use Openspring\SpringphpFramework\Web\RestController;
use Openspring\SpringphpFramework\Http\ResponseEntity;
use Openspring\SpringphpFramework\Http\HttpStatus;

class UserController extends RestController
{
    /**
     * @Autowired(true)
     * @var UserService
     */
    private $userService;

    /**
     * @GetMapping(value = "/users", consumes = "application/json", produces = "application/json")
     */
    public function getUsers()
    {
        $users = $this->userService->findAll();
        return ResponseEntity::ok($users);
    }

    /**
     * @GetMapping(value = "/users/{$id}", consumes = "application/json", produces = "application/json")
     */
    public function getUser($id)
    {
        $user = $this->userService->findById($id);
        return ResponseEntity::ok($user);
    }

    /**
     * @PostMapping(value = "/users", consumes = "application/json", produces = "application/json")
     */
    public function createUser(RequestBody $body)
    {
        $user = $this->userService->create($body);
        return ResponseEntity::status(HttpStatus::CREATED, $user);
    }
}

3. Create a service

<?php

use Openspring\SpringphpFramework\Core\Stereotype\Service;

/**
 * @Service
 */
class UserService extends Service
{
    /**
     * @Autowired(true)
     * @var UserDao
     */
    private $userDao;

    public function findAll(): array
    {
        return $this->userDao->findAll()->getResultList();
    }

    public function findById(int $id)
    {
        return $this->userDao->findById($id);
    }
}

4. Create an entity

<?php

use Openspring\SpringphpFramework\Orm\DaoSupport;
use Openspring\SpringphpFramework\Orm\Column;
use Openspring\SpringphpFramework\Orm\Type\DataType;
use Openspring\SpringphpFramework\Orm\Type\PrimaryKey;

class UserDao extends DaoSupport
{
    public $id;
    public $name;
    public $email;
    public $password;
    public $createdAt;

    public function __construct()
    {
        parent::__construct('#__users');

        $this->id        = new Column($this, 'id', 11, DataType::INT, new PrimaryKey());
        $this->name      = new Column($this, 'name', 100, DataType::VARCHAR);
        $this->email     = new Column($this, 'email', 150, DataType::VARCHAR, null, true);
        $this->password  = new Column($this, 'password', 255, DataType::HASH);
        $this->createdAt = new Column($this, 'created_at', 0, DataType::DATETIME);

        $this->initialize();
    }
}

Documentation

Application Bootstrap

The framework follows a structured boot sequence:

  1. SpringphpApplication constructor loads application.properties and sets up the Environment
  2. ApplicationContext::initialize() reads the .context cache (bean registry + route mappings)
  3. DatabaseContext::initialize() configures data sources
  4. On HTTP request: Router::route() matches the URL, then RestController::runAction() dispatches the action

REST Controllers

Controllers extend RestController and use docblock annotations for routing:

/**
 * @GetMapping(value = "/products/{$id}", produces = "application/json")
 */
public function getProduct($id) { ... }

/**
 * @PostMapping(value = "/products", consumes = "application/json", produces = "application/json")
 */
public function createProduct(RequestBody $body) { ... }

/**
 * @PutMapping(value = "/products/{$id}", consumes = "application/json", produces = "application/json")
 */
public function updateProduct($id, RequestBody $body) { ... }

/**
 * @DeleteMapping(value = "/products/{$id}", produces = "application/json")
 */
public function deleteProduct($id) { ... }

Available mapping annotations: @RequestMapping, @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PatchMapping

Parameter binding: RequestBody, RequestHeader, RequestCookie, RequestTemporaryFile

Dependency Injection

The IoC container manages beans as lazy-loaded singletons (default) or prototypes.

/**
 * @Service
 */
class OrderService extends Service
{
    /**
     * @Autowired(true)
     * @var OrderDao
     */
    private $orderDao;

    /**
     * @Autowired(true)
     * @var NotificationService
     */
    private $notificationService;

    /** @Value("app.order.max-items") */
    private $maxItems;
}
  • @Autowired(true) — injects a bean by type (field injection); requires @var on a separate line
  • @Value("property.key") — injects a value from application.properties
  • Stereotypes: @Component, @Service, @Configuration
  • Cyclic dependency detection is built in

ORM & Entities

Entities extend DaoSupport and define typed columns:

class ProductDao extends DaoSupport
{
    public $id;
    public $name;
    public $price;
    public $active;

    public function __construct()
    {
        parent::__construct('#__products');

        $this->id     = new Column($this, 'id', 11, DataType::INT, new PrimaryKey());
        $this->name   = new Column($this, 'name', 200, DataType::VARCHAR, null, false, true);
        $this->price  = new Column($this, 'price', 10, DataType::FLOAT);
        $this->active = new Column($this, 'active', 1, DataType::BOOLEAN);

        $this->initialize();
    }
}

Supported column types: INT, VARCHAR, FLOAT, BOOLEAN, DATE, DATETIME, JSON, GUID, HASH

The #__ prefix in table names is automatically replaced by the configured table prefix from your data source.

Query Builder

Fluent API for building SQL queries with parameter binding:

$results = $productDao->select(['id', 'name', 'price'])
    ->from()
    ->where('active', '=', 1)
    ->and('price', '>', ':minPrice')
    ->setParameter(':minPrice', 25.00)
    ->orderBy('name', 'ASC')
    ->getResultList();

Validation

Constraint-based validation using docblock annotations on method parameters:

ConstraintDescription
@NotNullValue must not be null
@NotBlankString must not be null or empty
@NotEmptyCollection/string must not be empty
@EmailMust be a valid email address
@Min(value)Numeric minimum
@Max(value)Numeric maximum
@Size(min, max)String/array length bounds
@Regex(pattern)Must match the regular expression
@Positive / @PositiveOrZeroMust be positive (or zero)
@Negative / @NegativeOrZeroMust be negative (or zero)
@Past / @PastOrPresentDate must be in the past
@Future / @FutureOrPresentDate must be in the future
@AssertTrue / @AssertFalseBoolean assertions
@URLMust be a valid URL
@InArray(values)Must be one of the specified values

Security (JWT)

JWT-based authentication with role and group access control:

/**
 * @GetMapping(value = "/admin/dashboard", produces = "application/json", groupIn = "1,2", roleIn = "ADMIN")
 */
public function dashboard() { ... }
  • groupIn — comma-separated list of authorized group IDs ("*" for all authenticated users)
  • roleIn — comma-separated list of authorized roles ("*" for all authenticated users)

Implement IJwtProcessor to handle token extraction and validation, and IUser for the current user model.

Configuration

Configuration is managed through properties files:

# application.properties
#################### COMMON CONFIG #########################
# application display name
springphp.application.name=appName
# project mode; supported (values: DEV|QUALIF|VAL|PPROD|REHEARSAL|PROD)
springphp.profile.active=DEV
# relative web home url; put / if the application is on the www root or something like /app1/ in other case
springphp.application.context.path=/relative/path/to/api/
# base is {rootPath}: application absolute public path
springphp.path.public=/relative/path/to/public/directory/
# base is {rootPath}: main sources relative path
springphp.path.sources=src/main/php/
# base is {rootPath}: test sources relative path
springphp.path.tests=src/test/php/
#################### DATABASE CONFIG ####################
# databases config names to connect to on application startup (config names must be separated by commas)
# Exemples: defaultDatabase, customersDatabase, clientsDatabase, commandesDatabase
springphp.database.targets=defaultDatabase
####################### LOG CONFIG #########################
# enable log. If false all logging is disabled !
springphp.log.enable=false
# error logging file path
springphp.log.file=logs\server_%d.log
# log file max size in KO (example: 1024 ; 2048)
springphp.log.file.maxsize=1024
# log level [SQL | DEBUG | INFO | WARN | ERROR]
springphp.log.level=SQL
# log output format: known var names: %d (date) ; %l (log level) ; %m (message)
springphp.log.format=[%d][%ns][%c][%l] %m
# application.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<orm-configuration>
  <datasources>
    <!-- default datasource -->
    <datasource id="defaultDatabase">
      <!-- connection parametrs -->
      <dialect>Openspring\SpringphpFramework\Orm\Driver\Mysql\MysqlDriver</dialect>
      <host>localhost</host>
      <database>db_name</database>
      <port>3306</port>
      <user>root</user>
      <password></password>
      <tableprefix>tbl_</tableprefix>
    </datasource>
  </datasources>
  <!-- show sql when executing queries -->
  <show_sql>true</show_sql>
</orm-configuration>

Profile-based configuration: Use dev-application.properties, test-application.properties, or prod-application.properties for environment-specific overrides.

Access properties in code:

// Via annotation
/** @Value("app.name") */
private $appName;

// Programmatically
$value = Environment::getProperty("app.name");

Interceptors

Implement HttpInterceptor for cross-cutting concerns like logging, auth checks, or metrics:

/**
 * @Component
 */
class AuthInterceptor extends HttpInterceptor
{
    public function preHandle(): bool
    {
        // Return false to halt the request
        return true;
    }

    public function postHandle(): bool
    {
        return true;
    }
}

Logging

Built-in logging with LogFactory:

$logger = LogFactory::getLogger(self::class);

$logger->info('Processing order');
$logger->debug('Order details: ' . $orderId);
$logger->error('Payment failed');

Testing

# Run all tests
vendor/bin/phpunit --configuration phpunit.xml

# Run a specific test class
vendor/bin/phpunit --filter UserServiceTest

# Run a specific test method
vendor/bin/phpunit --filter testCreateUser

# Run with coverage
vendor/bin/phpunit --coverage-text --configuration phpunit.xml

The framework provides a TestRunner that bootstraps the application in TEST profile and allows simulating HTTP requests. A Mockito-style mocking utility is also available:

use Openspring\SpringphpFramework\Mock\Mockito;

$mock = Mockito::mock(UserService::class);
Mockito::when($mock, 'findById')->thenReturn($expectedUser);

Project Structure

springphp-framework/
├── src/
│   ├── main/php/Openspring/SpringphpFramework/
│   │   ├── Annotation/       # Docblock annotation parsing
│   │   ├── Context/          # ApplicationContext, DatabaseContext, Environment
│   │   ├── Core/             # Bean, Component, Service, SpringphpApplication
│   │   ├── Enumeration/      # Framework enums
│   │   ├── Exception/        # Exception hierarchy (HTTP, Bean, Data, IO)
│   │   ├── Http/             # Request, Response, ResponseEntity, converters
│   │   ├── IO/               # File and directory utilities
│   │   ├── Log/              # Logger and Log4p implementation
│   │   ├── Mail/             # Mail sender
│   │   ├── Mock/             # Mockito-style test mocking
│   │   ├── Orm/              # DaoSupport, Column, QueryBuilder, drivers
│   │   ├── Reflection/       # ClassReflection for docblock parsing
│   │   ├── Security/         # JWT processing, access control
│   │   ├── Type/             # ArrayList, Any, DynamicObject
│   │   ├── Utils/            # Strings, Arrays, Dates, Encryption, etc.
│   │   ├── Validation/       # Constraint-based validation
│   │   └── Web/              # RestController, Router, RestTemplate
│   └── test/php/TestCase/    # Unit tests (mirrors main structure)
├── composer.json
├── phpunit.xml
└── LICENSE.txt

The project follows a Maven-style directory layout with PSR-4 autoloading.

Contributing

There are currently a number of outstanding issues that need to be addressed. Please read How to contribute.

You can report bugs and request features on the issue tracker.

License

This project is licensed under the MIT License. See LICENSE.txt for details.

openspring/springphp-framework 适用场景与选型建议

openspring/springphp-framework 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 208 次下载、GitHub Stars 达 1, 最近一次更新时间为 2019 年 11 月 03 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「framework」 「orm」 「rest」 「api」 「oauth」 「web service」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 openspring/springphp-framework 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 openspring/springphp-framework 我们能提供哪些服务?
定制开发 / 二次开发

基于 openspring/springphp-framework 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 208
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 2
  • 点击次数: 17
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 1
  • Watchers: 0
  • Forks: 0
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2019-11-03