Composer安装、入门+Autoload机制详解

一、安装

【comoser.phar】

comoser.phar是 composer的可执行文件,当前目录安装composer实际是【LINUX/WIN】curl(下载)命令 下载了composer.phar文件并且以php composer.phar [command] 的形式使用composer。

【首选-全局安装】

如果需要全局安装composer

WIN:

下载并使用composer-setup.exe安装程序或者crul命令手动安装(手动安装并不会检查该系统是否满足composer的安装先决条件)

curl -s https://getcomposer.org/composer.phar -o $HOME/local/bin/composer
chmod +x $HOME/local/bin/composer

路径 $HOME/local/bin (或是你选择的路径) 应该在你的 $PATH 环境变量中。这将会影响 composer 这个命令是否可用.

当你遇到文档指出执行 Composer 的命令是 php composer.phar install时,你可以使用下面命令替代:

composer install

LINUX:

先crul命令下载composer.phar,然后把可执行文件复制到**/usr/local/bin**路径中:

mv composer.phar /usr/local/bin/composer

注意: 以上命令如果失败,请尝试使用sudo 来增加权限。

【使用方法】

本地 使用 Composer 的话,你可以运行 php composer.phar ,全局的话是:composer

【其他安装方式-参考】

要快速安装Composer到当前目录,在终端中运行以下脚本. 若要自动安装, 请使用 以编程方式安装 Composer 的指南.

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"

此安装脚本会简单检查一些php.ini设置 , 警告你如果它们设置不正确,并会下载最新的composer.phar到当前目录。上面3行将会顺序执行:

  • 下载安装程序到当前目录
  • 运行安装程序
  • 删除安装程序

二、composer.json文件配置详解

1.配置文件

在我们开始一个项目的时候,首先会给项目取一个名字,我们暂且叫 电子商城,代号
eshop
。首先要写一个 Composer 的配置文件,来描述项目,为此,在项目的根目录下,建立文件名为 composer.json 的配置文件。内容如下:

【composer.json/json文件/键值对形式/值可对象】

{
    "name":             "dmlk31/eshop",
    "description":      "another e-commerce website",
    "keywords":         ["dmlk31", "online shop", "good", "eshop"],
    "homepage":         "http://www.xxx.com",
    "time":             "2014-12-30",
    "license":          "MIT",
    "authors": [{
        "name":         "dmlk31",
        "email":        "dmlk31@xxxx.com",
        "homepage":     "http://www.xxxx.com",
        "role":         "Engineer"
    }]
}

如果您熟悉 JSON 格式,那么上面这段内容不言而喻。事实上,这些键值对都是可选的。也就是说,可以都不写。但是如果要把项目打包成公共包发布,那么这些还是需要写上的,给你的包取个名字总不为过。让我们来过一下这些键值对的意义吧。

1.1【常见属性详解】

1)“name” : “dmlk31/eshop”=>“name":"作者/包名称"
name 表示包的名称。如果你经常在 Github 上混,那这个值的表达方式一定非常熟悉啦。解释下,通常包名包含两部分,并且以 / 分隔。斜杆前面部分,代表包的所有者。目前大部分的包作者都喜欢用 Github 的用户名作为这部分的值。斜杆后面部分代表包的名称。尽量保持简单和有意义些,便于记忆和传播。大部分情况下,很多人会用 Github 的代码库名称来命名,当然,这种情况下,代码要存在 Github 比较有意义。

2)"description" : “another e-commerce website”=>”descirption“:"应用简介(一般不写中文,私认为中文有可能会因为json文件的编码引起内容不可读现象)"
应用简介,这部分尽量简洁介绍下项目,别长篇大论。如果确实有很多话要说,那么可以写在 README.md 文件里。

3)"keywords" : [“dmlk31”, “online shop”, “good”, “eshop”](搜索用索引)
关键词的值是一个字符串数组,在发布成公用库的是时候,作为元数据信息,有利于包的搜索和发现

4)"homepage" : “http://www.xxx.com”(任意网站告诉用户这个包跟这个网站有关)
主页,可以放你想放的任何页面地址

5)"license" : “MIT”(可以设置当下php的项目的开放程度,开放程度以及标识符参照下方许可协议标准网站)
如果你决定将包公开发布,那么记得选择一个合适的许可证。这样别的程序员在引用包的时候,通过查看许可证,确保没有法律上的问题。可选,但强烈建议提供此内容。更多许可协议的标识符请参见 SPDX Open Source License Registry

6)"authors" : [{}](作者)
**作者**字段可以包含一个对象数组,也就是说可以提供多个作者信息。

7)version(版本信息设定)

version**不是必须的并且建议忽略**(见下文)。

它应该符合 ‘X.Y.Z’ 或者 ‘vX.Y.Z’ 的形式, -dev-patch-alpha-beta-RC 这些后缀是可选的。在后缀之后也可以再跟上一个数字。

例:

  • 1.0.0
  • 1.0.2
  • 1.1.0
  • 0.2.5
  • 1.0.0-dev
  • 1.0.0-alpha3
  • 1.0.0-beta2
  • 1.0.0-RC5

通常,我们能够从 VCS (git, svn, hg) 的信息推断出包的版本号,在这种情况下,我们建议忽略 version

注意: Packagist 使用 VCS 仓库, 因此 version 定义的版本号必须是真实准确的。 自己手动指定的 version,最终有可能在某个时候因为人为错误造成问题。

8)type 安装类型(规定安装方式)

包的安装类型,默认为 library

包的安装类型,用来定义安装逻辑。如果你有一个包需要一个特殊的逻辑,你可以设定一个自定义的类型。这可以是一个 symfony-bundle,一个 wordpress-plugin 或者一个 typo3-module。这些类型都将是具体到某一个项目,而对应的项目将要提供一种能够安装该类型包的安装程序。

composer 原生支持以下4种类型:

  • library: 这是默认类型,它会简单的将文件复制到 vendor 目录。-------简单安装依赖至vendor
  • project: 这表示当前包是一个项目,而不是一个库。例:框架应用程序 Symfony standard edition,内容管理系统 SilverStripe installer 或者完全成熟的分布式应用程序。使用 IDE 创建一个新的工作区时,这可以为其提供项目列表的初始化。
  • metapackage: 当一个空的包,包含依赖并且需要触发依赖的安装,这将不会对系统写入额外的文件。因此这种安装类型并不需要一个 dist 或 source。
  • composer-plugin: 一个安装类型为 composer-plugin 的包,它有一个自定义安装类型,可以为其它包提供一个 installler。详细请查看 自定义安装类型

仅在你需要一个自定义的安装逻辑时才使用它。建议忽略这个属性,采用默认的 library。
9)time(版本发布时间)

版本发布时间。

必须符合 YYYY-MM-DDYYYY-MM-DD HH:MM:SS 格式。

可选。

1.2【常见Package links详解】

用于指导composer install命令的安装范围和方式

下面提到的所有对象,都应该是 包名 到 版本 的**映射对象(通过简单参数/配置指导编程行为)**。

【require/require-dev】

实例:

{
    "require": {
        "monolog/monolog": "1.0.*"
    }
}

所有的这些都是可选的。

requirerequire-dev 还支持稳定性标签(@,仅针对“root 包”)。这允许你在 minimum-stability 设定的范围外做进一步的限制或扩展。例:如果你想允许依赖一个不稳定的包,你可以在一个包的版本约束后使用它,或者是一个空的版本约束内使用它。(可以依赖一些不稳定的包)

tips:【Root 包】

“root 包”是指由 composer.json 定义的在你项目根目录的包。这是 composer.json 定义你项目所需的主要条件。(简单的说,你自己的项目就是一个 root 包)

某些字段仅适用于“root 包”上下文。 config 字段就是其中一个例子。只有“root 包”可以定义。在依赖包中定义的 config 字段将被忽略,这使得 config 字段只有“root 包”可用(root-only)。

如果你克隆了其中的一个依赖包,直接在其上开始工作,那么它就变成了“root 包”。与作为他人的依赖包时使用相同的 composer.json 文件,但上下文发生了变化。

注意: 一个资源包是不是“root 包”,取决于它的上下文。 例:如果你的项目依赖 monolog 库,那么你的项目就是“root 包”。 但是,如果你从 GitHub 上克隆了 monolog 为它修复 bug, 那么此时 monolog 就是“root 包”。root包是相当于第二个app应用,不被依赖,担任项目的部分或主要功能模块

实例:

{
    "require": {
        "monolog/monolog": "1.0.*@beta",
        "acme/foo": "@dev"
    }
}

如果你的依赖之一,有对另一个不稳定包的依赖,你最好在 require 中显示的定义它,并带上足够详细的稳定性标识。

实例:

{
    "require": {
        "doctrine/doctrine-fixtures-bundle": "dev-master",
        "doctrine/data-fixtures": "@dev"
    }
}

requirerequire-dev 还支持对 dev(开发)版本的明确引用(即:版本控制系统中的提交编号 commit),以确保它们被锁定到一个给定的状态,即使你运行了更新命令。你只需要明确一个开发版本号,并带上诸如 #<ref> 的标识。

tips:【开发版本号】

不管是什么版本管理工具,每一条提交记录都会有一个对应的版本号,一般是一个整数,git是一个hash字符串。不管怎样,这个版本号是唯一的,有时候我们在程序运行的时候会在日志里面输出程序的版本号,或者在命令行运行的时候在控制台中输出当前程序的版本号。一般而言,如果我们程序输出的版本号,与版本控制系统源码对应的版本号有关联关系是最好不过的,这样当运行的程序出现问题的时候,可以通过程序的版本号,去源码的版本控制系统中找到对应的源码进行分析,也就是说我们知道当前运行的程序对应在版本控制系统中的源码。

通过git命令得到版本号

这里的使用环境是Linux,我们的源码客户端是git,通过git命令我们可以得到当前最新版本库中的源码版本,使用git log 命令的格式化输出,可以得到每次提交结果中的各个部分,例如版本号,提交时间,提交日志。git log 命令默认情况下会输出所有提交记录的详细信息,通过使用其提供的–pretty选项我们可以指定git log 输出我们需要的部分,例如代表版本号的hash字符串部分。

git log --pretty=format:"%H"

实例:

{
    "require": {
        "monolog/monolog": "dev-master#2eb0c0978d290a1c45346a1955188929cb4e5db7",
        "acme/foo": "1.0.x-dev#abc123"
    }
}

注意: 虽然这有时很方便,但不应该长期在你的包中使用,因为它有一个技术上的限制。 composer.json 将仍然在哈希值之前指定的分支名称读取元数据, 正因为如此,在某些情况下,它不会是一个实用的解决方法, 如果可能,你应该总是尝试切换到拥有标签的版本。

它也可以应用于行内别名,这样它将匹配一个约束,否则不会。更多信息请参考 别名

require

必须的软件包列表,除非这些依赖被满足,否则不会完成安装。

require-dev (root-only)

这个列表是为开发或测试等目的,额外列出的依赖“root 包”的 require-dev 默认是会被安装的(如果直接composer install的话)。然而 installupdate 支持使用 --no-dev 参数来跳过 require-dev 字段中列出的包。

【conflict】

一些没有明确规定的包和此列表中的包有版本冲突时。该列表中的这个版本不会被安装

请注意,在 conflict 中指定类似于 <1.0, >= 1.1 的版本范围时,这表示它与小于1.0 并且 同时大等于1.1的版本冲突,这很可能不是你想要的。在这种情况下你可能想要表达的是 <1.0 | >= 1.1

实例

{
        "name": "symfony/http-kernel",
        "version": "v4.4.20",
        ...
        "conflict": {
            "symfony/browser-kit": "<4.3",
            "symfony/config": "<3.4",
            "symfony/console": ">=5",
            "symfony/dependency-injection": "<4.3",
            "symfony/translation": "<4.2",
            "twig/twig": "<1.43|<2.13,>=2"
        },

很容易猜测“symfony/console”5.0与"symfony/http-kernel4.4.20冲突,因此symfony/console 5.0将不会安装。

【replace】

这个列表中的包将被当前包取代。这使你可以 fork 一个包,以不同的名称和版本号发布,同时要求依赖于原包的其它包,在这之后依赖于你 fork 的这个包,因为它取代了原来的包。(改变自动加载机制原有的依赖类)

这对于创建一个内部包含子包的主包也非常的有用。例如 symfony/symfony 这个主包,包含了所有 Symfony 的组件,而这些组件又可以作为单独的包进行发布。如果你 require 了主包,那么它就会自动完成其下各个组件的任务,因为主包取代了子包。

注意,在使用上述方法取代子包时,通常你应该只对子包使用 self.version 这一个版本约束,以确保主包仅替换掉子包的准确版本,而不是任何其他版本。

【provide】—这个没有接触相关介绍和使用暂时不清楚

List of other packages that are provided by this package. This is mostly useful for common interfaces. A package could depend on some virtual logger package, any library that implements this logger interface would simply list it in provide.

【suggest】

建议安装的包,它们增强或能够与当前包良好的工作。这些只是信息,并显示在依赖包安装完成之后,给你的用户一个建议,他们可以添加更多的包。

格式如下,版本约束变成了描述信息。

实例:

{
    "suggest": {
        "monolog/monolog": "Allows more advanced logging of the application flow"
    }
}

三、依赖管理

在 composer.json 文件里增加一个新的字段:require。这个字段的值是一个对象,同样以键值对的形式构成。以上述提到的两个依赖位置,写成 Composer 管理的方式如下:

【require】

"require": {
    "phpoffice/phpexcel"      : "dev-master",
    "swiftmailer/swiftmailer" : "5.3.*@dev"
}

以 swiftmailer 为例,(name)swiftmailer/swiftmailer 代表的是包名称5.3.*@dev ([版本大小] [版本类型])是版本信息(此处tips,composer .json本身是有version属性以提供版本号的,但是composer自己可以通过svn/git等版本控制工具得到版本信息,同时手动编写容易格式错误所以一般在composer.json中忽略version属性)。合起来的意思就是说,我们将要开发的应用,依赖于 swiftmailer 的 5.3.* 版本。其中:

5.3.*(版本号) 表示:可以使用 5.3.1 版本,也可以使用 5.3.2 版本,Composer 在获取的时候,将寻找 5.3 版本下最新的版本。版本号支持一些更加宽泛的约束,比如 >=1.0, >=1.0, <2.0,更加具体的信息可以查看:http://docs.phpcomposer.com/01-basic-usage.md#The-require-Key

@dev(版本类型=>开发/测试/生产) 表示可以获取开发版本。通常,开发版本意味非稳定版本,很可能存在bug。稳定性标签可以作用于特定的依赖项,也可以作用于全局。

默认情况下,Composer 只会获取最新稳定版本,如果这个例子我们不加 @dev 约束,而 5.3.* 版本都是开发版本,那么在获取的时候 Composer 就会报错,指出改版本不符合要求。如果确定这个开发版本没有问题,那么就可以通过加@dev,让 Composer 获取这个开发版本。

版本类型设置)全局稳定性设置:通过设置 minimum-stability 的值,来告诉 Composer 当前开发的项目的依赖要求的包的全局稳定性级别,它的值包括:dev、alpha、beta、RC、stable,stable 是默认值。

至此,两个依赖添加完毕,我们可以运行下 Composer 包更新命令,看看效果啦:

composer install------默认安装require内所有依赖

成功运行完毕,会在根目录下发现 vendor 文件夹,里面包含了刚刚我们列出来的两个包文件代码。

三、自动加载

自此,Composer 已经帮我们把需要的库文件下载下来啦,接下去想到的就是如何引用这些库文件。最简单的方式就是 require 或者 include,但这就不够高大上了啊,需要花时间去库文件里查看需要引入哪些文件,费事而且容易出错。好在 Composer 可以帮我们解决这个问题。那就是 autoload。

【简单使用autoload,composer管理的依赖会自动加入自动加载信息中,但是下方说的是自定义的依赖】

在运行完 composer install 命令后,怎么调用 PHPExcel 库呢?很简单,只要引入 vendor 目录下的 autoload.php 文件就可以了。可以在根目录下,建一个 index.php 文件,加入一下内容:(在主要PHP方法中include引入即可)

include 'vendor/autoload.php'
$excel = new PHPExcel();
var_dump($excel);

用浏览器访问一下这个页面,就会发现 PHPExcel 对象已经被成功创建啦,是不是很方便?

【自定义依赖:在composer.json中加入autoload字段,用的是file字段的单文件加载】

其实到目前为止,我们并没用在 composer.json 文件里加入 autoload 字段,那么什么时候需要加入呢? ***那就是当我们想让 Composer 帮我们自动加载我们自己定义的类的时候***。例如,我们自己写了个订单管理类,取名 OrderManager,放在 lib 目录下的 OrderManager.php 文件里。内容如下:

class OrderManager
{
public function test()
{
echo ‘hello’;
}
}
那么如何让 Composer 帮我们自动加载这个类呢? 在 composer.json 里加入下面的内容?

3.1 使用 files 方式(通常作为函数库的载入方式,而非类库)

"autoload":{
    "files" : ["lib/OrderManager.php"]
}

files 键对应的值是一个数组,数组元素是文件的路径,路径是相对于应用的根目录。加上上述内容后,运行命令:

$ composer dump-autoload
让 Composer 重建自动加载的信息(让composer根据映射信息更新自动加载信息),完成之后,就可以在 index.php 里调用 OrderManager 类啦。

【自定义依赖:classmap-类到文件的对应关系,将映射从单文件加载变成了类到文件夹加载】

3.2 使用 classmap 方式
通过文件引入的方法虽然直观,但是很费劲,每个文件都得引入一次,实在不是好的解决办法。有没有更好的办法呢?尝试将 autoload 的值改成:

"classmap" : [“lib”]
再此运行 composer dump-autoload,尝试调用,依然能够成功创建 OrderManager 类。

其实,classmap 通过建立类到文件的对应关系,当程序需要 OrderManager 类时,Composer 的自动加载类通过查找 OrderManager 类所在的文件,然后再将改文件 include 进来。

因此,这又导致了一个问题,那就是每加一个新类,就需要运行一次 composer dump-autoload 来创建类到文件到对应关系,比 files 方法虽然好一点,但是还是很不够舒爽啊!于是,PSR-0 出场了。先了解下什么是 PSR-0。

【自动加载规范:PSR0(tips:命名空间(文件夹)的映射)/4 加载方式】

FIG 组织制定的一组 PHP 相关规范,简称 PSR,其中:

PSR-0 自动加载

PSR-1 基本代码规范

PSR-2 代码样式

PSR-3 日志接口

PSR-4 自动加载

目前就这五个规范,乍一看,PSR-0 和 PSR-4 是重复了,实际上,在功能上确实有所重复。区别在于 PSR-4 的规范比较干净,去除了兼容 PHP 5.3 以前版本的内容,有一点 PSR-0 升级版的感觉。当然,PSR-4 也不是要完全替代 PSR-0,而是在必要的时候补充 PSR-0。

简而言之,就是希望通过一组约定的目录,文件名,类名定义方式,来实现快速通过类查找到文件,然后包含进来,实现自动加载。
PSR-4 和 PSR-0 最大的区别是对下划线(underscore)的定义不同。PSR-4 中,在类名中使用下划线没有任何特殊含义。而 PSR-0 则规定类名中的下划线_会被转化成目录分隔符。

不管是 PSR-0 还是 PSR-4,都要求有个命名空间,所以我们需要对 OrderManager 类进行一些小的修改,加上命名空间:

namespace EShopLib;
class OrderManager
{
    public function test()
    {
        echo "hello";
    }
 }

同时,文件夹的结构也要修改成:lib\EShopLib\OrderManager.php

然后修改 composer.json 里的 autoload 部分如下:

"autoload":{
   "psr-0":{
       "EShopLib" : "lib/"
   }
}

这里需要注意的是,EShopLib 是命名空间,lib 是目录名,他们的组合告诉 Composer,文件搜索是在:lib/EShopLib/ 目录下,而不是在 EShopLib/lib 目录下,这一点要特别注意,有点绕,容易弄错。

如果我们把命名空间改成 EShop\lib, 相应的目录结构要改成:lib\EShop\lib\OrderManager.php,autoload 部分的写法相应的也要改成:

"autoload":{
    "psr-0":{
        "EShop\\lib" : "lib/"//\\是转义符
    }
}

好了,那我们试试再加一个类,然后不用运行 composer dump-autoload 命令,看看新类是否能加载上。在 lib 目录下,新增一个 ShipManager.php 文件,内容如下:

namespace EShop\lib;
class ShipManager
{
    public function test()
    {
        echo 'hello ship class';
    }
}

尝试在 index.php 文件中调用:

$orderMgr = new Silk\lib\OrderManager();
$orderMgr->test();
$shipMgr = new Silk\lib\ShipManager();
$shipMgr->test();

运行成功,说明使用 PSR-0 规范进行自动加载,比 classmap 更加方便。下面试试 PSR-4 方式,整理下目录结构,改成:lib\OrderManager.php,修改命名空间为 EShop, 修改 autoload 部分为:

"autoload":{
    "psr-4":{
        "EShop":"lib"
    }
}

尝试调用,发现报错 Fatal error: Uncaught exception ‘InvalidArgumentException’ with message ‘A non-empty PSR-4 prefix must end with a namespace separator. 提示要加上分隔符"\",那就加上吧:

"autoload":{
    "psr-4":{
        "Silk\\":"lib"
    }
}

再次 composer dump-autoload,运行测试,OK通过!

psr-4(tips:命名空间的映射)


关键词 “必须”(“MUST”)、“一定不可/一定不能”(“MUST NOT”)、“需要”(“REQUIRED”)、 “将会”(“SHALL”)、“不会”(“SHALL NOT”)、“应该”(“SHOULD”)、“不该”(“SHOULD NOT”)、 “推荐”(“RECOMMENDED”)、“可以”(“MAY”)和”可选“(“OPTIONAL”)的详细描述可参见 [RFC 2119][] 。

1. 概述

本 PSR 是关于由文件路径 [自动载入][http://tools.ietf.org/html/rfc2119] 对应类的相关规范, 本规范是可互操作的,可以作为任一自动载入规范的补充,其中包括 PSR-0,此外, 本 PSR 还包括自动载入的类对应的文件存放路径规范。

2. 详细说明

  1. 此处的“类”泛指所有的class类、接口、traits可复用代码块以及其它类似结构。

  2. 一个完整的类名需具有以下结构:

    \<命名空间>(\<子命名空间>)*\<类名>1
    

    i. 完整的类名 必须 要有一个顶级命名空间,被称为 “vendor namespace”;

    ii. 完整的类名 可以 有一个或多个子命名空间;

    iii. 完整的类名 必须 有一个最终的类名;

    iv. 完整的类名中任意一部分中的下滑线都是没有特殊含义的;

    v. 完整的类名 可以 由任意大小写字母组成;

    vi. 所有类名都 必须 是大小写敏感的。

3.当根据完整的类名载入相应的文件……

i. 完整的类名中,去掉最前面的命名空间分隔符,前面连续的一个或多个命名空间和子命名空间,作为“命名空间前缀”,其 必须 与至少一个“文件基目录”相对应;

ii. 紧接命名空间前缀后的子命名空间 必须 与相应的”文件基目录“相匹配,其中的命名空间分隔符将作为目录分隔符。

iii. 末尾的类名 必须 与对应的以 .php 为后缀的文件同名。

iv. 自动加载器(autoloader)的实现 一定不能 抛出异常、一定不能 触发任一级别的错误信息以及不应该有返回值。

3. 例子

下表展示了符合规范完整类名、命名空间前缀和文件基目录所对应的文件路径。

完整类名命名空间前缀文件基目录文件路径
\Acme\Log\Writer\File_WriterAcme\Log\Writer./acme-log-writer/lib/./acme-log-writer/lib/File_Writer.php
\Aura\Web\Response\StatusAura\Web/path/to/aura-web/src//path/to/aura-web/src/Response/Status.php
\Symfony\Core\RequestSymfony\Core./vendor/Symfony/Core/./vendor/Symfony/Core/Request.php
\Zend\AclZend/usr/includes/Zend//usr/includes/Zend/Acl.php

关于本规范的实现,可参阅 相关实例
注意:实例并不属于规范的一部分,且随时会有所变动。

  • 0
    点赞
  • 0
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:游动-白 设计师:我叫白小胖 返回首页
评论

打赏作者

Runnings Man

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值