Maison  >  Article  >  développement back-end  >  Simplifiez l'intégration des startups avec PHP et OAuth

Simplifiez l'intégration des startups avec PHP et OAuth

WBOY
WBOYoriginal
2023-09-03 09:53:06889parcourir

使用 PHP 和 OAuth 简化初创公司的入职培训

Ce tutoriel fait partie de la série « Construisez votre startup avec PHP » sur Envato Tuts+. Dans cette série, je vais vous guider tout au long du lancement de votre startup, du concept à la réalité, en utilisant mon application Meeting Planner comme exemple concret. À chaque étape du processus, je publierai le code de Meeting Planner comme exemple open source dont vous pourrez tirer des leçons. J'aborderai également les problèmes commerciaux liés aux startups qui se posent.

Dans ce tutoriel, je vais vous guider dans l'intégration d'OAuth avec les réseaux sociaux courants pour rendre l'enregistrement et la réutilisation plus faciles et plus efficaces. J'explorerai Facebook, Google, Twitter et LinkedIn, les réseaux qui, à mon avis, sont les mieux adaptés au public cible de Meeting Planner.

Tout le code de Meeting Planner est écrit à l’aide du framework PHP Yii2. Si vous souhaitez en savoir plus sur Yii2, consultez notre série parallèle Programmation avec Yii2 sur Envato Tuts+.

Si vous n’avez pas encore essayé Meeting Planner, essayez de planifier votre première réunion aujourd’hui. Cette année, ça commence vraiment à se mettre en place. Finalement, j'ai pu utiliser le support AuthClient intégré de Yii2 pour fournir une connexion à partir de tous les réseaux ci-dessus - afin que vous puissiez vous inscrire en utilisant ces réseaux dès le départ.

Commentaires bienvenus. Si vous avez des questions ou des suggestions de sujets, veuillez laisser un commentaire ci-dessous. Vous pouvez également me joindre sur Twitter @reifman.

Qu'est-ce qu'AuthClient ?

AuthClient est la prise en charge intégrée de Yii permettant à votre application de s'authentifier auprès de services tiers à l'aide d'OpenID, OAuth ou OAuth2.

Si vous suiviez ma série Yii2 en juin 2015, vous m'auriez vu utiliser AuthClient pour intégrer Google via OpenID, mais la société a mis fin au support de la spécification peu de temps après. Puis, en décembre, j'ai écrit un tutoriel utilisant l'extension Yii2-User pour ajouter le support Google OAuth - quelque chose que le framework Yii2 n'avait pas encore. Cependant, Yii2-User ne s'intègre pas bien aux bases de code établies qui disposent déjà de bases de codes destinées aux utilisateurs. Mais heureusement, le framework Yii2 a ajouté la prise en charge de Google OAuth et tout devient plus simple.

Dans ce tutoriel, je vais vous guider dans l'utilisation de la nouvelle fonctionnalité AuthClient à intégrer à divers réseaux sociaux populaires. Prêt à l'emploi, Yii fournit une assistance pour les clients suivants :

  • Facebook
  • GitHub
  • Google
  • LinkedIn
  • Microsoft en direct
  • Twitter

Une autre motivation pour prendre en charge la connexion à Meeting Planner via les réseaux sociaux est qu'il permet aux gens de se présenter et de partager facilement leur nom et leur adresse e-mail avec nous. En s'inscrivant par e-mail et mot de passe, nous n'apprenons jamais leur nom. Cependant, contrairement à d’autres réseaux sociaux, Twitter a créé d’importantes barrières pour obtenir les adresses e-mail des utilisateurs, ce qui m’a finalement amené à le désactiver temporairement.

Commençons l’intégration du code.

Installez AuthClient dans notre application

Tout d'abord, nous devons installer le composant Yii pour OAuth, à savoir AuthClient de Yii.

Ajouter AuthClient à Composer

Ajoutons la bibliothèque AuthClient à composer.json :

    "minimum-stability": "stable",
    "require": {
        "php": ">=5.4.0",
        "yiisoft/yii2": "*",
        "yiisoft/yii2-bootstrap": "*",
        "yiisoft/yii2-swiftmailer": "*",
        "2amigos/yii2-google-maps-library": "*",
        "2amigos/yii2-google-places-library": "*",
        "stichoza/google-translate-php": "~2.0",
        "2amigos/yii2-date-time-picker-widget": "*",
        "yiisoft/yii2-jui": "*",
        "cebe/yii2-gravatar": "*",
        "kartik-v/yii2-widget-fileinput": "*",
        "kartik-v/yii2-widget-switchinput": "*",
        "yiisoft/yii2-imagine": "*",
        "2amigos/yii2-resource-manager-component": "0.1.*",
        "yiisoft/yii2-authclient": "~2.0.0"
            },

Ensuite, nous devons mettre à jour Composer :

sudo composer update
Password:
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Updating 2amigos/yii2-date-time-picker-widget (0.1.0 => 0.1.1)
    Checking out 572e2448ba1cd207b339dd5d117e3d1d23f0bbc3

  - Installing yiisoft/yii2-authclient (2.0.2)
    Loading from cache

Writing lock file
Generating autoload files

Configurer la prise en charge d'AuthClient

Et nous devons ajouter les paramètres de configuration AuthClient au fichier de configuration dans frontendconfigmain.php.

Ajoutez des éléments de tableau pour tous les services tiers que vous souhaitez prendre en charge (les détails de chaque service peuvent être trouvés dans le guide AuthClient) :

'components' => [
      'authClientCollection' => [
              'class' => 'yii\authclient\Collection',
              'clients' => [
                  'facebook' => [
                      'class' => 'yii\authclient\clients\Facebook',
                      'clientId' => $config['oauth_fb_id'],
                      'clientSecret' => $config['oauth_fb_secret'],
                  ],
                  'google' => [
                      'class' => 'yii\authclient\clients\GoogleOAuth',
                      'clientId' => $config['oauth_google_client_id'],
                      'clientSecret' => $config['oauth_google_client_secret'],
                    ],
                  'linkedin' => [
                      'class' => 'yii\authclient\clients\LinkedIn',
                      'clientId' => $config['linkedin_client_id'],
                      'clientSecret' => $config['linkedin_client_secret'],
                  ],
                  'twitter' => [
                      'class' => 'yii\authclient\clients\Twitter',
                      'consumerKey' => $config['oauth_twitter_key'],
                      'consumerSecret' => $config['oauth_twitter_secret'],
                              ],
              ],
          ],

Afin d'obtenir les codes de toutes ces clés et secrets, vous devez enregistrer votre application auprès de chaque réseau social. Cela prend souvent beaucoup de temps.

Enregistrer l'application développeur

Suivez-moi pendant que je vous guide à travers l'enregistrement de certains réseaux et une configuration plus approfondie d'autres.

Inscrivez-vous avec Twitter

Créez une nouvelle application Twitter sur le tableau de bord de l'application Twitter :

使用 PHP 和 OAuth 简化初创公司的入职培训

Cliquez sur Créer une nouvelle application - J'ai trouvé l'URL de rappel inutile, mais j'utilise maintenant l'espace réservé http://mydomain.com/user/security/auth.

使用 PHP 和 OAuth 简化初创公司的入职培训

Voici la nouvelle page de notre application :

使用 PHP 和 OAuth 简化初创公司的入职培训

Voici la page Paramètres :

使用 PHP 和 OAuth 简化初创公司的入职培训

Voici la page Clés et jetons d'accès. Ici, nous devons copier la Consumer Key (API Key) et Consumer Secret (API Secret)  :

使用 PHP 和 OAuth 简化初创公司的入职培训

这些密钥位于我们的 mp.ini 文件中,该文件被读入 $config 变量上面为 Twitter 配置 AuthClient。

注册我们的 Facebook 应用程序

接下来,让我们访问 Facebook 开发者控制台并添加新应用

使用 PHP 和 OAuth 简化初创公司的入职培训

我们现在选择创建一个WWW网站应用:

使用 PHP 和 OAuth 简化初创公司的入职培训

提供我们的应用程序名称:

使用 PHP 和 OAuth 简化初创公司的入职培训

并收集我们的新应用 ID

使用 PHP 和 OAuth 简化初创公司的入职培训

他们要求提供所有常规信息,例如网址:

使用 PHP 和 OAuth 简化初创公司的入职培训

然后您可以在列表中找到我们的Meeting Planner应用程序:

使用 PHP 和 OAuth 简化初创公司的入职培训

以下是您的应用程序的 Facebook 仪表板:

使用 PHP 和 OAuth 简化初创公司的入职培训

向 Google 注册

Google API 比 Twitter 和 Facebook 稍微复杂一些,因此用户体验有点难以遵循。但基本上,一旦您创建了应用程序,您就需要 OAuth 2.0 密钥,您可以通过在凭据屏幕上打开应用程序区域来获取该密钥:

使用 PHP 和 OAuth 简化初创公司的入职培训

这将带您到这里:

使用 PHP 和 OAuth 简化初创公司的入职培训

出于安全原因,Google(和 LinkedIn)需要完整列表,详细说明在执行过程中可能会使用哪些 URL 路径和参数。 OAuth 序列。在开发过程中,这可能需要大量调整 - 即使是从本地主机进行测试也是如此。

输入后,您将看到下面列出的内容:

使用 PHP 和 OAuth 简化初创公司的入职培训

Google 在帮助您配置同意屏幕方面做得很好,您的用户在尝试注册或链接时将看到该屏幕他们的 Google 帐户与会议策划者的关系:

使用 PHP 和 OAuth 简化初创公司的入职培训

通过 LinkedIn 注册

与 Google 相比,LinkedIn 相当简单。您需要申请的基本详细信息:

使用 PHP 和 OAuth 简化初创公司的入职培训

与 Google 一样,它们需要您在开发和生产中使用的所有网址。您还可以在此页面获取密钥:

使用 PHP 和 OAuth 简化初创公司的入职培训

将密钥放入我们的配置文件

在从 GitHub 中保护您的密钥中,我详细描述了如何使用配置文件来存储除 GitHub 存储库之外的所有密钥。然后,我将此文件包含在 Yii 配置文件的开头。这可以防止我意外地将我的密钥签入我的存储库并危及我的帐户。

我们将 Twitter 和 Facebook 应用程序密钥和机密放入存储库外部的 /var/secure/mp.ini 中:

oauth_fb_id="154xxxxxxxxxxxxxx33"
oauth_fb_secret="bcxxxxxxxxxxxxxxdda"
oauth_twitter_key ="JCpxxxxxxxxxxxxxxnsF"
oauth_twitter_secret="f3xxxxxxxxxxxxxxxxxxxxxxxxxxxxu37"
oauth_twitter_token="153xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxfBj"
oauth_twitter_token_secret="Synxxxxxxxxxxxxxxxxxxxxxxxxxxxx4X"
oauth_google_client_id = "1xxxxxxxxxxxxxxxxxxxxxxq.apps.googleusercontent.com"
oauth_google_client_secret = "cfkxxxxxxxxxxxxxxox"
linkedin_client_id = "7xxxxxxxxxxxxxxq"
linkedin_client_secret ="IxxxxxxxxxxxxxxI"

这里又是 \frontend\config\main.php 中的代码,其中包含这些设置并设置各个配置变量:

<?php
$config = parse_ini_file('/var/secure/mp.ini', true);

$params = array_merge(
    require(__DIR__ . '/../../common/config/params.php'),
    require(__DIR__ . '/../../common/config/params-local.php'),
    require(__DIR__ . '/params.php'),
    require(__DIR__ . '/params-local.php')
);

return [
    'id' => 'app-frontend',
    'basePath' => dirname(__DIR__),
    'bootstrap' => ['log'],
    'controllerNamespace' => 'frontend\controllers',
    'components' => [
      'authClientCollection' => [
              'class' => 'yii\authclient\Collection',
              'clients' => [
                  'google' => [
                      'class' => 'yii\authclient\clients\GoogleOpenId'
                  ],
                  'facebook' => [
                      'class' => 'yii\authclient\clients\Facebook',
                      'clientId' => $config['oauth_fb_id'],
                      'clientSecret' => $config['oauth_fb_secret'],
                  ],
                  'twitter' => [
                      'class' => 'yii\authclient\clients\Twitter',
                      'consumerKey' => $config['oauth_twitter_key'],
                      'consumerSecret' => $config['oauth_twitter_secret'],
                              ],
              ],
          ],
      'urlManager' => [

更新架构以存储会话密钥

现在我们已经准备好编写代码来集成社交注册和登录,我们需要数据库来创建一个 Auth 表,该表将存储社交服务、该人的 ID 以及会议规划器中该人员的 user_id

./yii migrate/create create_auth_table
Yii Migration Tool (based on Yii v2.0.2)

Create new migration '/Users/Jeff/Sites/mp/console/migrations/m150227_235635_create_auth_table.php'? (yes|no) [no]:yes
New migration created successfully.

迁移如下所示:

<?php

use yii\db\Schema;
use yii\db\Migration;

class m150227_235635_create_auth_table extends Migration
{
   public function up()
   {
       $tableOptions = null;
       if ($this->db->driverName === 'mysql') {
           $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
       }

       $this->createTable('{{%auth}}', [
           'id' => Schema::TYPE_PK,
           'user_id' => Schema::TYPE_BIGINT.' NOT NULL',
           'source' => Schema::TYPE_STRING.' NOT NULL',
           'source_id' => Schema::TYPE_STRING.' NOT NULL',
       ], $tableOptions);
       $this->addForeignKey('fk-auth-user_id-user-id', '{{%auth}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');
   }

   public function down()
   {
     $this->dropForeignKey('fk-auth-user_id-user-id', '{{%auth}}');
     $this->dropTable('{{%auth}}');
   }
}

这是我们运行时的结果:

./yii migrate/up
Yii Migration Tool (based on Yii v2.0.2)

Total 1 new migration to be applied:
    m150227_235635_create_auth_table

Apply the above migration? (yes|no) [no]:yes
*** applying m150227_235635_create_auth_table
    > create table {{%auth}} ... done (time: 0.016s)
    > add foreign key fk-auth-user_id-user-id: {{%auth}} (user_id) references {{%user}} (id) ... done (time: 0.012s)
*** applied m150227_235635_create_auth_table (time: 0.033s)

Migrated up successfully.

我再次使用 Yii 的代码生成器 Gii 来创建 Auth 模型:

使用 PHP 和 OAuth 简化初创公司的入职培训

最终,Auth 表将包含如下内容:

使用 PHP 和 OAuth 简化初创公司的入职培训

将 AuthChoice 小部件添加到会议规划器

Yii2 的 AuthChoice 小部件在为您配置的每个服务实现登录按钮方面做得非常出色。它按照您设置服务和密钥数组的顺序执行此操作(以便您可以更改它)。

将小部件添加到我们的表单(login.php 和 signup.php)非常简单:

<div class="row">
        <div class="col-lg-5">
          <p>Or, login with one of the following services:</p>
          <?= yii\authclient\widgets\AuthChoice::widget([
               'baseAuthUrl' => ['site/auth','mode'=>'login'],
               'popupMode' => false,
          ]) ?>
        </div> <!-- end col-lg-5 -->
      </div> <!-- end row -->

现在这是我们的注册页面:

使用 PHP 和 OAuth 简化初创公司的入职培训

对于已登录的现有用户,我为他们创建了一种简单的方法来关联其帐户。它在个人资料设置页面上称为关联社交帐户

使用 PHP 和 OAuth 简化初创公司的入职培训

如果您点击LinkedIn这是他们的 OAuth 屏幕,要求您授予会议策划者权限:

使用 PHP 和 OAuth 简化初创公司的入职培训

这是 Google 的屏幕:

使用 PHP 和 OAuth 简化初创公司的入职培训

但是当用户允许我们分享他们的社交帐户详细信息时,到底发生了什么?让我们看一下我编写的用于处理用户操作的代码。

处理 OAuth 权限

\frontend\controllers\SiteController.php 处理传入的 auth 操作到函数 onAuthSuccess

    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'error' => [
                'class' => 'yii\web\ErrorAction',
            ],
            'captcha' => [
                'class' => 'yii\captcha\CaptchaAction',
                'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null,
            ],
            'auth' => [
                'class' => 'yii\authclient\AuthAction',
                'successCallback' => [$this, 'onAuthSuccess'],
            ],
        ];
    }

大多数优秀的 OAuth 客户端都会在类似的属性数组中提供类似的信息,Twitter 除外。 Twitter 在共享电子邮件地址方面起步较晚,对于我的 MVP 来说,现在不值得进行额外的工作来配置它。 Google 和 Facebook 更为流行。

首先,我正在收集服务详细信息并收集尽可能多的个人数据:电子邮件、名字和姓氏、全名,尤其是该社交网络中该用户的外部 ID:

public function onAuthSuccess($client)
        {
          $mode =  Yii::$app->getRequest()->getQueryParam('mode');
          $attributes = $client->getUserAttributes();
          $serviceId = $attributes['id'];
          $serviceProvider = $client->getId();
          $serviceTitle = $client->getTitle();
          $firstname ='';
          $lastname='';
          $fullname ='';
          switch ($serviceProvider) {
            case 'facebook':
              $username = $email = $attributes['email'];
              $fullname = $attributes['name'];
              break;
            case 'google':
              $email = $attributes['emails'][0]['value'];
              if (isset($attributes['displayName'])) {
                  $fullname = $username = $attributes['displayName'];
              }
              if (isset($attributes['name']['familyName']) and isset($attributes['name']['givenName'])) {
                $lastname = $attributes['name']['familyName'];
                $firstname = $attributes['name']['givenName'];
              }
            break;
            case 'linkedin':
              $username = $email = $attributes['email-address'];
              $lastname = $attributes['first-name'];
              $firstname = $attributes['last-name'];
              $fullname = $firstname.' '.$lastname;
            break;
            case 'twitter':
              $username = $attributes['screen_name'];
              $fullname = $attributes['name'];
              // to do - fix social helpers
              $email = $serviceId.'@twitter.com';
            break;
          }
          // to do - split names into first and last with parser
            $auth = Auth::find()->where([
                'source' => (string)$serviceProvider,
                'source_id' => (string)$serviceId,
            ])->one();

在上面的最后几行代码中,我们在 Auth 表中搜索此人的外部 ID。如果它们不存在,则它们对于 Meeting Planner 来说是新的。如果它们存在,我们就会认出它们。

同样,我们需要检查他们的电子邮件地址是否已存在,因为该电子邮件地址的人可能之前已在 Meeting Planner 中注册过。

当 MeetingPlanner.io 当前没有经过身份验证的用户时,下面的代码将查看传入的用户数据。

如果外部 ID 已在我们的 Auth 表中,我们会将其登录。这对他们来说很简单!

如果我们无法识别该 ID,但我们已经注册了电子邮件地址,我们会要求他们通过用户名和密码登录,然后关联他们的帐户。

if (Yii::$app->user->isGuest) {
    if ($auth) {
      // if the user_id associated with this oauth login is registered, try to log them in
      $user_id = $auth->user_id;
      $person = new \common\models\User;
      $identity = $person->findIdentity($user_id);
      Yii::$app->user->login($identity);
    } else {
      // it's a new oauth id
      // first check if we know the email address
      if (isset($email) && User::find()->where(['email' => $email])->exists()) {
        // the email is already registered, ask person to link accounts after logging in
        Yii::$app->getSession()->setFlash('error', [
            Yii::t('frontend', "The email in this {client} account is already registered. Please login using your username and password first, then link to this account in your profile settings.", ['client' => $serviceTitle]),
        ]);
        $this->redirect(['login']);
      } else {
        if ($mode == 'login') {
          // they were trying to login with an unconnected account - ask them to login normally and link after
          Yii::$app->getSession()->setFlash('error', [
              Yii::t('frontend', "We don't recognize the user with this email from {client}. If you wish to sign up, try again below. If you wish to link {client} to your Meeting Planner account, login first with your username and password. Then visit your profile settings.", ['client' => $serviceTitle]),
          ]);
          $this->redirect(['signup']);
        }

使用 PHP 和 OAuth 简化初创公司的入职培训

接下来,如果他们在点击社交帐户按钮后进入登录页面,并且我们无法识别外部 ID或电子邮件地址,我们将他们重定向到注册页面并要求他们从注册页面重试。

如果他们从注册页面进行链接,我们将确保新用户不会面临(先前存在的 Meeting Planner 用户的)重复用户名的风险。在这种情况下,我们暂时用随机字符串扩展用户名。我们使用密码将他们注册为 Meeting Planner 的新用户(他们实际上并不需要密码)。

else if ($mode == 'signup') {
 // sign up a new account using oauth
 // look for username that exists already and differentiate it
 if (isset($username) && User::find()->where(['username' => $username])->exists()) {
   $username.=Yii::$app->security->generateRandomString(6);
 }
 $password = Yii::$app->security->generateRandomString(12);
   $user = new User([
       'username' => $username, // $attributes['login'],
       'email' => $email,
       'password' => $password,
       'status' => User::STATUS_ACTIVE,
   ]);
   $user->generateAuthKey();
   $user->generatePasswordResetToken();
   $transaction = $user->getDb()->beginTransaction();
   if ($user->save()) {
       $auth = new Auth([
           'user_id' => $user->id,
           'source' => $serviceProvider, // $client->getId(),
           'source_id' => $serviceId, // (string)$attributes['id'],
       ]);
       if ($auth->save()) {
           $transaction->commit();
           Yii::$app->user->login($user);
       } else {
           print_r($auth->getErrors());
       }
   } else {
       print_r($user->getErrors());
   }
 } // end signup
}
}

在上述最后步骤中,我们将其外部社交帐户详细信息添加到 Auth  表中,以供将来识别。

关联现有会议策划者帐户

如果它们来自用户个人资料页面(而不是我们的登录或注册页面)上的“链接社交帐户”选项卡,那么我们只需将其外部帐户详细信息添加到 Auth ,并将其登录名移至 User::STATUS_ACTIVE。 (请记住,从会议策划者邀请链接到达但从未注册的用户具有 User::STATUS_PASSIVE 模式。)

} else {
  // user already logged in, link the accounts
    if (!$auth) { // add auth provider
        $auth = new Auth([
            'user_id' => Yii::$app->user->id,
            'source' => $serviceProvider,
            'source_id' => $serviceId,
        ]);
        $auth->validate();
        $auth->save();
        $u = User::findOne(Yii::$app->user->id);
        $u->status = User::STATUS_ACTIVE;
        $u->update();
        Yii::$app->session->setFlash('success', Yii::t('frontend', 'Your {serviceProvider} account has been connected to your Meeting Planner account. In the future you can log in with a single click of its logo.',
array('serviceProvider'=>$serviceTitle)));
    }
}

看起来是这样的(将来我会从 OAuth 信息中填写命名信息——还没有完成):

使用 PHP 和 OAuth 简化初创公司的入职培训

Fin

Je dois admettre que l'impact de l'établissement de connexions OAuth avec des services majeurs comme Google, Facebook et LinkedIn est assez énorme. Cela facilite l'inscription et l'utilisation régulière de Meeting Planner et accélère l'authentification future. C'est en effet un peu incroyable.

Les planificateurs de réunions se sont vraiment réunis au cours des derniers mois. Veuillez essayer la connexion et l'inscription sur les réseaux sociaux maintenant ! Consultez les prochains didacticiels de notre série Construire votre startup avec PHP - il y aura de nombreuses fonctionnalités intéressantes à mesure que le produit évolue vers MVP.

J'ai également commencé à essayer WeFunder à la lumière de la mise en œuvre des nouvelles règles de financement participatif de la SEC. Veuillez envisager de suivre notre profil. J'en parlerai probablement davantage dans le cadre de notre série.

N'hésitez pas à ajouter vos questions et commentaires ci-dessous ; je participerai généralement à la discussion. Vous pouvez également me joindre sur Twitter @reifman. J’accepte les demandes de fonctionnalités et de thèmes.

Si vous voulez savoir quand le prochain tutoriel Yii2 sortira, suivez-moi sur Twitter @reifman ou consultez ma page instructeur. Ma page instructeur contiendra immédiatement tous les articles de cette série.

Liens connexes

  • Meeting Planner : planifiez votre première réunion
  • Aperçu du financement du planificateur de réunions
  • Programmation avec Yii2 : Pour commencer
  • Guide des extensions AuthClient Yii 2

  • Documentation de la bibliothèque Yii2 OAuth2
  • Réunion d'échange des développeurs Yii2

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn