PHPのJWTライブラリについて調べてみた

調査対象

  • firebase/php-jwt
  • lcobucci/jwt
  • web-token/jwt-framework
  • kelvinmo/simplejwt
  • sop/jwx

調査結果

firebase/php-jwt

https://packagist.org/packages/firebase/php-jwt

最新バージョン: v6.4.0 2023-02-09 21:01 UTC

php: ^7.1||^8.0

JWS: ○ JWE: × JWK: ○

サンプルコード
JWS HS256
$key = 'example_key';
$payload = [
    'iss' => 'http://example.org',
    'aud' => 'http://example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $key, 'HS256');

$decoded = JWT::decode($jwt, new Key($key, 'HS256'));
サンプルコード
JWS RS256
$privateKey = <<<EOD
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAuzWHNM5f+amCjQztc5QTfJfzCC5J4nuW+L/aOxZ4f8J3Frew
M2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJhzkPYLae7bTVro3hok0zDITR8F6S
JGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548tu4czCuqU8BGVOlnp6IqBHhAswNMM
78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vSopcT51koWOgiTf3C7nJUoMWZHZI5
HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTzTTqo1SCSH2pooJl9O8at6kkRYsrZ
WwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/BwQIDAQABAoIBAFtGaOqNKGwggn9k
6yzr6GhZ6Wt2rh1Xpq8XUz514UBhPxD7dFRLpbzCrLVpzY80LbmVGJ9+1pJozyWc
VKeCeUdNwbqkr240Oe7GTFmGjDoxU+5/HX/SJYPpC8JZ9oqgEA87iz+WQX9hVoP2
oF6EB4ckDvXmk8FMwVZW2l2/kd5mrEVbDaXKxhvUDf52iVD+sGIlTif7mBgR99/b
c3qiCnxCMmfYUnT2eh7Vv2LhCR/G9S6C3R4lA71rEyiU3KgsGfg0d82/XWXbegJW
h3QbWNtQLxTuIvLq5aAryV3PfaHlPgdgK0ft6ocU2de2FagFka3nfVEyC7IUsNTK
bq6nhAECgYEA7d/0DPOIaItl/8BWKyCuAHMss47j0wlGbBSHdJIiS55akMvnAG0M
39y22Qqfzh1at9kBFeYeFIIU82ZLF3xOcE3z6pJZ4Dyvx4BYdXH77odo9uVK9s1l
3T3BlMcqd1hvZLMS7dviyH79jZo4CXSHiKzc7pQ2YfK5eKxKqONeXuECgYEAyXlG
vonaus/YTb1IBei9HwaccnQ/1HRn6MvfDjb7JJDIBhNClGPt6xRlzBbSZ73c2QEC
6Fu9h36K/HZ2qcLd2bXiNyhIV7b6tVKk+0Psoj0dL9EbhsD1OsmE1nTPyAc9XZbb
OPYxy+dpBCUA8/1U9+uiFoCa7mIbWcSQ+39gHuECgYAz82pQfct30aH4JiBrkNqP
nJfRq05UY70uk5k1u0ikLTRoVS/hJu/d4E1Kv4hBMqYCavFSwAwnvHUo51lVCr/y
xQOVYlsgnwBg2MX4+GjmIkqpSVCC8D7j/73MaWb746OIYZervQ8dbKahi2HbpsiG
8AHcVSA/agxZr38qvWV54QKBgCD5TlDE8x18AuTGQ9FjxAAd7uD0kbXNz2vUYg9L
hFL5tyL3aAAtUrUUw4xhd9IuysRhW/53dU+FsG2dXdJu6CxHjlyEpUJl2iZu/j15
YnMzGWHIEX8+eWRDsw/+Ujtko/B7TinGcWPz3cYl4EAOiCeDUyXnqnO1btCEUU44
DJ1BAoGBAJuPD27ErTSVtId90+M4zFPNibFP50KprVdc8CR37BE7r8vuGgNYXmnI
RLnGP9p3pVgFCktORuYS2J/6t84I3+A17nEoB4xvhTLeAinAW/uTQOUmNicOP4Ek
2MsLL2kHgL8bLTmvXV4FX+PXphrDKg1XxzOYn0otuoqdAQrkK4og
-----END RSA PRIVATE KEY-----
EOD;

$publicKey = <<<EOD
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuzWHNM5f+amCjQztc5QT
fJfzCC5J4nuW+L/aOxZ4f8J3FrewM2c/dufrnmedsApb0By7WhaHlcqCh/ScAPyJ
hzkPYLae7bTVro3hok0zDITR8F6SJGL42JAEUk+ILkPI+DONM0+3vzk6Kvfe548t
u4czCuqU8BGVOlnp6IqBHhAswNMM78pos/2z0CjPM4tbeXqSTTbNkXRboxjU29vS
opcT51koWOgiTf3C7nJUoMWZHZI5HqnIhPAG9yv8HAgNk6CMk2CadVHDo4IxjxTz
TTqo1SCSH2pooJl9O8at6kkRYsrZWwsKlOFE2LUce7ObnXsYihStBUDoeBQlGG/B
wQIDAQAB
-----END PUBLIC KEY-----
EOD;

$payload = [
    'iss' => 'example.org',
    'aud' => 'example.com',
    'iat' => 1356999524,
    'nbf' => 1357000000
];

$jwt = JWT::encode($payload, $privateKey, 'RS256');

$decoded = JWT::decode($jwt, new Key($publicKey, 'RS256'));
ソースサイズ
$ phploc src/
phploc 7.0.2 by Sebastian Bergmann.

Size
  Lines of Code (LOC)                             1308
  Comment Lines of Code (CLOC)                     415 (31.73%)
  Non-Comment Lines of Code (NCLOC)                893 (68.27%)
  Logical Lines of Code (LLOC)                     277 (21.18%)
    Classes                                        277 (100.00%)
      Average Class Length                          39
        Minimum Class Length                         0
        Maximum Class Length                       140
      Average Method Length                          6
        Minimum Method Length                        1
        Maximum Method Length                       22
      Average Methods Per Class                      5
        Minimum Methods Per Class                    0
        Maximum Methods Per Class                   16
    Functions                                        0 (0.00%)
      Average Function Length                        0
    Not in classes or functions                      0 (0.00%)

Cyclomatic Complexity
  Average Complexity per LLOC                     0.46
  Average Complexity per Class                   19.14
    Minimum Class Complexity                      1.00
    Maximum Class Complexity                     74.00
  Average Complexity per Method                   4.53
    Minimum Method Complexity                     1.00
    Maximum Method Complexity                    20.00

Dependencies
  Global Accesses                                    0
    Global Constants                                 0 (0.00%)
    Global Variables                                 0 (0.00%)
    Super-Global Variables                           0 (0.00%)
  Attribute Accesses                                73
    Non-Static                                      63 (86.30%)
    Static                                          10 (13.70%)
  Method Calls                                      77
    Non-Static                                      27 (35.06%)
    Static                                          50 (64.94%)

Structure
  Namespaces                                         1
  Interfaces                                         0
  Traits                                             0
  Classes                                            7
    Abstract Classes                                 0 (0.00%)
    Concrete Classes                                 7 (100.00%)
      Final Classes                                  0 (0.00%)
      Non-Final Classes                              7 (100.00%)
  Methods                                           36
    Scope
      Non-Static Methods                            13 (36.11%)
      Static Methods                                23 (63.89%)
    Visibility
      Public Methods                                18 (50.00%)
      Protected Methods                              0 (0.00%)
      Private Methods                               18 (50.00%)
  Functions                                          0
    Named Functions                                  0 (0.00%)
    Anonymous Functions                              0 (0.00%)
  Constants                                          8
    Global Constants                                 0 (0.00%)
    Class Constants                                  8 (100.00%)
      Public Constants                               0 (0.00%)
      Non-Public Constants                           8 (100.00%)

lcobucci/jwt

https://packagist.org/packages/lcobucci/jwt

最新バージョン: 5.0.0 2023-02-25 21:35 UTC

php: ~8.1.0 || ~8.2.0

JWS: ○ JWE: × JWK: ×

サンプルコード
JWS
$key = InMemory::base64Encoded(
    'hiG8DlOKvtih6AxlZn5XKImZ06yu8I3mkOzaJrEuW8yAv8Jnkw330uMt8AEqQ5LB'
);

$token = (new JwtFacade())->issue(
    new Sha256(),
    $key,
    static fn (
        Builder $builder,
        DateTimeImmutable $issuedAt
    ): Builder => $builder
        ->issuedBy('https://api.my-awesome-app.io')
        ->permittedFor('https://client-app.io')
        ->expiresAt($issuedAt->modify('+10 minutes'))
);

$jwt = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2NTg2OTYwNTIsIm5iZiI6MT'
    . 'Y1ODY5NjA1MiwiZXhwIjoxNjU4Njk2NjUyLCJpc3MiOiJodHRwczovL2FwaS5teS1hd2Vzb'
    . '21lLWFwcC5pbyIsImF1ZCI6Imh0dHBzOi8vY2xpZW50LWFwcC5pbyJ9.yzxpjyq8lXqMgaN'
    . 'rMEOLUr7R0brvhwXx0gp56uWEIfc';

$token = (new JwtFacade())->parse(
    $jwt,
    new Constraint\SignedWith(new Sha256(), $key),
    new Constraint\StrictValidAt(
        new FrozenClock(new DateTimeImmutable('2022-07-24 20:55:10+00:00'))
    )
);
ソースサイズ
$ phploc src
phploc 7.0.2 by Sebastian Bergmann.

Directories                                          9
Files                                               69

Size
  Lines of Code (LOC)                             2977
  Comment Lines of Code (CLOC)                     438 (14.71%)
  Non-Comment Lines of Code (NCLOC)               2539 (85.29%)
  Logical Lines of Code (LLOC)                     419 (14.07%)
    Classes                                        419 (100.00%)
      Average Class Length                           6
        Minimum Class Length                         0
        Maximum Class Length                        41
      Average Method Length                          1
        Minimum Method Length                        0
        Maximum Method Length                       12
      Average Methods Per Class                      3
        Minimum Methods Per Class                    0
        Maximum Methods Per Class                   14
    Functions                                        0 (0.00%)
      Average Function Length                        0
    Not in classes or functions                      0 (0.00%)

Cyclomatic Complexity
  Average Complexity per LLOC                     0.24
  Average Complexity per Class                    2.46
    Minimum Class Complexity                      1.00
    Maximum Class Complexity                     16.00
  Average Complexity per Method                   1.52
    Minimum Method Complexity                     1.00
    Maximum Method Complexity                     5.00

Dependencies
  Global Accesses                                    0
    Global Constants                                 0 (0.00%)
    Global Variables                                 0 (0.00%)
    Super-Global Variables                           0 (0.00%)
  Attribute Accesses                                86
    Non-Static                                      86 (100.00%)
    Static                                           0 (0.00%)
  Method Calls                                     220
    Non-Static                                     141 (64.09%)
    Static                                          79 (35.91%)

Structure
  Namespaces                                        10
  Interfaces                                        16
  Traits                                             0
  Classes                                           53
    Abstract Classes                                 4 (7.55%)
    Concrete Classes                                49 (92.45%)
      Final Classes                                 49 (100.00%)
      Non-Final Classes                              0 (0.00%)
  Methods                                          238
    Scope
      Non-Static Methods                           196 (82.35%)
      Static Methods                                42 (17.65%)
    Visibility
      Public Methods                               201 (84.45%)
      Protected Methods                              5 (2.10%)
      Private Methods                               32 (13.45%)
  Functions                                          3
    Named Functions                                  0 (0.00%)
    Anonymous Functions                              3 (100.00%)
  Constants                                         42
    Global Constants                                 0 (0.00%)
    Class Constants                                 42 (100.00%)
      Public Constants                              30 (71.43%)
      Non-Public Constants                          12 (28.57%)

web-token/jwt-framework

https://packagist.org/packages/web-token/jwt-framework

最新バージョン: 3.1.7 2023-03-11 14:35 UTC

php: >=8.1

JWS: ○ JWE: ○ JWK: ○

サンプルコード
JWS HS256
// The algorithm manager with the HS256 algorithm.
$algorithmManager = new AlgorithmManager([
    new HS256(),
]);

// Our key.
$jwk = new JWK([
    'kty' => 'oct',
    'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
]);

// We instantiate our JWS Builder.
$jwsBuilder = new JWSBuilder($algorithmManager);

// The payload we want to sign. The payload MUST be a string hence we use our JSON Converter.
$payload = json_encode([
    'iat' => time(),
    'nbf' => time(),
    'exp' => time() + 3600,
    'iss' => 'My service',
    'aud' => 'Your application',
]);

$jws = $jwsBuilder
    ->create()                               // We want to create a new JWS
    ->withPayload($payload)                  // We set the payload
    ->addSignature($jwk, ['alg' => 'HS256']) // We add a signature with a simple protected header
    ->build();                               // We build it
// The algorithm manager with the HS256 algorithm.
$algorithmManager = new AlgorithmManager([
    new HS256(),
]);

// We instantiate our JWS Verifier.
$jwsVerifier = new JWSVerifier(
    $algorithmManager
);

// Our key.
$jwk = new JWK([
    'kty' => 'oct',
    'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
]);

// The serializer manager. We only use the JWS Compact Serialization Mode.
$serializerManager = new JWSSerializerManager([
    new CompactSerializer(),
]);

// The input we want to check
$token = 'eyJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1MDc4OTY5OTIsIm5iZiI6MTUwNzg5Njk5MiwiZXhwIjoxNTA3OTAwNTkyLCJpc3MiOiJNeSBzZXJ2aWNlIiwiYXVkIjoiWW91ciBhcHBsaWNhdGlvbiJ9.eycp9PTdgO4WA-68-AMoHPwsKDr68NhjIQKz4lUkiI0';

// We try to load the token.
$jws = $serializerManager->unserialize($token);

// We verify the signature. This method does NOT check the header.
// The arguments are:
// - The JWS object,
// - The key,
// - The index of the signature to check. See 
$isVerified = $jwsVerifier->verifyWithKey($jws, $jwk, 0);
JWE
// The key encryption algorithm manager with the A256KW algorithm.
$keyEncryptionAlgorithmManager = new AlgorithmManager([
    new A256KW(),
]);

// The content encryption algorithm manager with the A256CBC-HS256 algorithm.
$contentEncryptionAlgorithmManager = new AlgorithmManager([
    new A256CBCHS512(),
]);

// The compression method manager with the DEF (Deflate) method.
$compressionMethodManager = new CompressionMethodManager([
    new Deflate(),
]);

// We instantiate our JWE Builder.
$jweBuilder = new JWEBuilder(
    $keyEncryptionAlgorithmManager,
    $contentEncryptionAlgorithmManager,
    $compressionMethodManager
);

// Our key.
$jwk = new JWK([
    'kty' => 'oct',
    'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
]);

// The payload we want to encrypt. It MUST be a string.
$payload = json_encode([
    'iat' => time(),
    'nbf' => time(),
    'exp' => time() + 3600,
    'iss' => 'My service',
    'aud' => 'Your application',
]);

$jwe = $jweBuilder
    ->create()              // We want to create a new JWE
    ->withPayload($payload) // We set the payload
    ->withSharedProtectedHeader([
        'alg' => 'A256KW',        // Key Encryption Algorithm
        'enc' => 'A256CBC-HS512', // Content Encryption Algorithm
        'zip' => 'DEF'            // We enable the compression (irrelevant as the payload is small, just for the example).
    ])
    ->addRecipient($jwk)    // We add a recipient (a shared key or public key).
    ->build();              // We build it
// The key encryption algorithm manager with the A256KW algorithm.
$keyEncryptionAlgorithmManager = new AlgorithmManager([
    new A256KW(),
]);

// The content encryption algorithm manager with the A256CBC-HS256 algorithm.
$contentEncryptionAlgorithmManager = new AlgorithmManager([
    new A256CBCHS512(),
]);

// The compression method manager with the DEF (Deflate) method.
$compressionMethodManager = new CompressionMethodManager([
    new Deflate(),
]);

// We instantiate our JWE Decrypter.
$jweDecrypter = new JWEDecrypter(
    $keyEncryptionAlgorithmManager,
    $contentEncryptionAlgorithmManager,
    $compressionMethodManager
);

// Our key.
$jwk = JWK::create([
    'kty' => 'oct',
    'k' => 'dzI6nbW4OcNF-AtfxGAmuyz7IpHRudBI0WgGjZWgaRJt6prBn3DARXgUR8NVwKhfL43QBIU2Un3AvCGCHRgY4TbEqhOi8-i98xxmCggNjde4oaW6wkJ2NgM3Ss9SOX9zS3lcVzdCMdum-RwVJ301kbin4UtGztuzJBeg5oVN00MGxjC2xWwyI0tgXVs-zJs5WlafCuGfX1HrVkIf5bvpE0MQCSjdJpSeVao6-RSTYDajZf7T88a2eVjeW31mMAg-jzAWfUrii61T_bYPJFOXW8kkRWoa1InLRdG6bKB9wQs9-VdXZP60Q4Yuj_WZ-lO7qV9AEFrUkkjpaDgZT86w2g',
]);

// The serializer manager. We only use the JWE Compact Serialization Mode.
$serializerManager = new JWESerializerManager([
    new CompactSerializer(),
]);

// The input we want to decrypt
$token = 'eyJhbGciOiJBMjU2S1ciLCJlbmMiOiJBMjU2Q0JDLUhTNTEyIiwiemlwIjoiREVGIn0.9RLpf3Gauf05QPNCMzPcH4XNBLmH0s3e-YWwOe57MTG844gnc-g2ywfXt_R0Q9qsR6WhkmQEhdLk2CBvfqr4ob4jFlvJK0yW.CCvfoTKO9tQlzCvbAuFAJg.PxrDlsbSRcxC5SuEJ84i9E9_R3tCyDQsEPTIllSCVxVcHiPOC2EdDlvUwYvznirYP6KMTdKMgLqxB4BwI3CWtys0fceSNxrEIu_uv1WhzJg.4DnyeLEAfB4I8Eq0UobnP8ymlX1UIfSSADaJCXr3RlU';

// We try to load the token.
$jwe = $serializerManager->unserialize($token);

// We decrypt the token. This method does NOT check the header.
$success = $jweDecrypter->decryptUsingKey($jwe, $jwk, 0);
ソースサイズ
$ phploc src
phploc 7.0.2 by Sebastian Bergmann.

Directories                                         59
Files                                              375

Size
  Lines of Code (LOC)                            22879
  Comment Lines of Code (CLOC)                    2497 (10.91%)
  Non-Comment Lines of Code (NCLOC)              20382 (89.09%)
  Logical Lines of Code (LLOC)                    3943 (17.23%)
    Classes                                       3722 (94.40%)
      Average Class Length                          10
        Minimum Class Length                         0
        Maximum Class Length                       145
      Average Method Length                          2
        Minimum Method Length                        0
        Maximum Method Length                       31
      Average Methods Per Class                      4
        Minimum Methods Per Class                    0
        Maximum Methods Per Class                   29
    Functions                                      221 (5.60%)
      Average Function Length                        5
    Not in classes or functions                      0 (0.00%)

Cyclomatic Complexity
  Average Complexity per LLOC                     0.33
  Average Complexity per Class                    4.79
    Minimum Class Complexity                      1.00
    Maximum Class Complexity                     65.00
  Average Complexity per Method                   1.96
    Minimum Method Complexity                     1.00
    Maximum Method Complexity                    14.00

Dependencies
  Global Accesses                                    0
    Global Constants                                 0 (0.00%)
    Global Variables                                 0 (0.00%)
    Super-Global Variables                           0 (0.00%)
  Attribute Accesses                               719
    Non-Static                                     719 (100.00%)
    Static                                           0 (0.00%)
  Method Calls                                    3377
    Non-Static                                    2807 (83.12%)
    Static                                         570 (16.88%)

Structure
  Namespaces                                        41
  Interfaces                                        25
  Traits                                             0
  Classes                                          315
    Abstract Classes                                25 (7.94%)
    Concrete Classes                               290 (92.06%)
      Final Classes                                203 (70.00%)
      Non-Final Classes                             87 (30.00%)
  Methods                                         1399
    Scope
      Non-Static Methods                          1248 (89.21%)
      Static Methods                               151 (10.79%)
    Visibility
      Public Methods                              1001 (71.55%)
      Protected Methods                            194 (13.87%)
      Private Methods                              204 (14.58%)
  Functions                                         40
    Named Functions                                  0 (0.00%)
    Anonymous Functions                             40 (100.00%)
  Constants                                         64
    Global Constants                                 0 (0.00%)
    Class Constants                                 64 (100.00%)
      Public Constants                              49 (76.56%)
      Non-Public Constants                          15 (23.44%)

kelvinmo/simplejwt

https://packagist.org/packages/kelvinmo/simplejwt

最新バージョン: v0.7.0 2022-11-25 21:25 UTC

php: ^7.1 || ^8.0

JWS: ○ JWE: ○ JWK: ○

サンプルコード
JWS HS256
$set = new SimpleJWT\Keys\KeySet();
$key = new SimpleJWT\Keys\SymmetricKey('secret123', 'bin');
$set->add($key);

// Note $headers['alg'] is required
$headers = ['alg' => 'HS256', 'typ' => 'JWT'];
$claims = ['iss' => 'me', 'exp' => 1234567];
$jwt = new SimpleJWT\JWT($headers, $claims);

try {
    print $jwt->encode($set);
} catch (\RuntimeException $e) {

}

try {
    $jwt = SimpleJWT\JWT::decode('abc.def.ghigjghr', $set, 'HS256');
} catch (SimpleJWT\InvalidTokenException $e) {

}
JWE
// Note $headers['alg'] and $headers['enc'] are required
$headers = ['alg' => 'PBES2-HS256+A128KW', 'enc' => 'A128CBC-HS256'];
$plaintext = 'This is the plaintext I want to encrypt.';
$jwt = new SimpleJWT\JWE($headers, $plaintext);

try {
    print $jwt->encrypt($set);
} catch (\RuntimeException $e) {

}

try {
    $jwt = SimpleJWT\JWE::decrypt('abc.def.ghi.klm.nop', $set, 'PBES2-HS256+A128KW');
} catch (SimpleJWT\InvalidTokenException $e) {

}

print $jwt->getHeader('alg');
print $jwt->getPlaintext();
ソースサイズ
$ phploc src
phploc 7.0.2 by Sebastian Bergmann.

Directories                                          7
Files                                               38

Size
  Lines of Code (LOC)                             6467
  Comment Lines of Code (CLOC)                    2987 (46.19%)
  Non-Comment Lines of Code (NCLOC)               3480 (53.81%)
  Logical Lines of Code (LLOC)                    1273 (19.68%)
    Classes                                       1273 (100.00%)
      Average Class Length                          33
        Minimum Class Length                         0
        Maximum Class Length                       129
      Average Method Length                          5
        Minimum Method Length                        1
        Maximum Method Length                       74
      Average Methods Per Class                      6
        Minimum Methods Per Class                    0
        Maximum Methods Per Class                   18
    Functions                                        0 (0.00%)
      Average Function Length                        0
    Not in classes or functions                      0 (0.00%)

Cyclomatic Complexity
  Average Complexity per LLOC                     0.41
  Average Complexity per Class                   14.68
    Minimum Class Complexity                      1.00
    Maximum Class Complexity                     63.00
  Average Complexity per Method                   3.37
    Minimum Method Complexity                     1.00
    Maximum Method Complexity                    40.00

Dependencies
  Global Accesses                                    0
    Global Constants                                 0 (0.00%)
    Global Variables                                 0 (0.00%)
    Super-Global Variables                           0 (0.00%)
  Attribute Accesses                               256
    Non-Static                                     210 (82.03%)
    Static                                          46 (17.97%)
  Method Calls                                     494
    Non-Static                                     314 (63.56%)
    Static                                         180 (36.44%)

Structure
  Namespaces                                         8
  Interfaces                                         5
  Traits                                             1
  Classes                                           32
    Abstract Classes                                 4 (12.50%)
    Concrete Classes                                28 (87.50%)
      Final Classes                                  0 (0.00%)
      Non-Final Classes                             28 (100.00%)
  Methods                                          229
    Scope
      Non-Static Methods                           197 (86.03%)
      Static Methods                                32 (13.97%)
    Visibility
      Public Methods                               191 (83.41%)
      Protected Methods                             32 (13.97%)
      Private Methods                                6 (2.62%)
  Functions                                          6
    Named Functions                                  0 (0.00%)
    Anonymous Functions                              6 (100.00%)
  Constants                                         38
    Global Constants                                 0 (0.00%)
    Class Constants                                 38 (100.00%)
      Public Constants                              38 (100.00%)
      Non-Public Constants                           0 (0.00%)

sop/jwx

https://packagist.org/packages/sop/jwx

最新バージョン: 0.11.1 2021-09-27 06:57 UTC

php: >=7.2

JWS: ○ JWE: ○ JWK: ○

コメント

https://github.com/sop/jwx/issues/2 に脆弱性があるという報告があるが、脆弱性の内容と修正済みかどうかは公表されていませんでした。その後、0.12.0 で修正されました。

サンプルコード
JWS HS256
// compose claims set
$claims = new Claims(
    new IssuerClaim('John Doe'),
    new SubjectClaim('Jane Doe'),
    new AudienceClaim('acme-client'),
    IssuedAtClaim::now(),
    NotBeforeClaim::now(),
    ExpirationTimeClaim::fromString('now + 30 minutes'),
    new JWTIDClaim(UUIDv4::createRandom()->canonical()),
    new Claim('custom claim', ['any', 'values']));
// create a signed JWT using HS256 with "secret" as a password
$jwt = JWT::signedFromClaims($claims, new HS256Algorithm('secret'));
echo $jwt->token() . "\n";

// read JWT token from the first argument
$jwt = new JWT($argv[1]);
// key to use for signature validation
$jwk = SymmetricKeyJWK::fromKey('secret');
// create validation context
$ctx = ValidationContext::fromJWK($jwk)
    ->withIssuer('John Doe')
    ->withSubject('Jane Doe')
    ->withAudience('acme-client');
// get claims set from the JWT. signature shall be verified and claims
// validated according to validation context.
$claims = $jwt->claims($ctx);
// print all claims
foreach ($claims as $claim) {
    printf("%s: %s\n", $claim->name(), json_encode($claim->value()));
}
JWE
// compose claims set
$claims = new Claims(
    new Claim('secret data', 'for your eyes only'),
    IssuedAtClaim::now(),
    NotBeforeClaim::now(),
    ExpirationTimeClaim::fromString('now + 30 minutes'));
// load RSA public key
$jwk = RSAPublicKeyJWK::fromPEM(
    PEM::fromFile(dirname(__DIR__) . '/test/assets/rsa/public_key.pem'));
$key_algo = RSAESPKCS1Algorithm::fromPublicKey($jwk);
$enc_algo = new A128CBCHS256Algorithm();
// create an encrypted JWT token
$jwt = JWT::encryptedFromClaims($claims, $key_algo, $enc_algo);
echo $jwt->token() . "\n";

// read JWT token from the first argument
$jwt = new JWT($argv[1]);
// load RSA private key
$jwk = RSAPrivateKeyJWK::fromPEM(
    PEM::fromFile(dirname(__DIR__) . '/test/assets/rsa/private_key.pem'));
// create validation context containing only key for decryption
$ctx = ValidationContext::fromJWK($jwk)
    // NOTE: asymmetric key derivation algorithms are not enabled by default
    // due to sign/encrypt confusion vulnerability!
    ->withPermittedAlgorithmsAdded(JWA::ALGO_RSA1_5);
// decrypt claims from the encrypted JWT
$claims = $jwt->claims($ctx);
// print all claims
foreach ($claims as $claim) {
    printf("%s: %s\n", $claim->name(), json_encode($claim->value()));
}
ソースサイズ
$ phploc lib
phploc 7.0.2 by Sebastian Bergmann.

Directories                                         24
Files                                              145

Size
  Lines of Code (LOC)                            10643
  Comment Lines of Code (CLOC)                    4253 (39.96%)
  Non-Comment Lines of Code (NCLOC)               6390 (60.04%)
  Logical Lines of Code (LLOC)                    1387 (13.03%)
    Classes                                       1387 (100.00%)
      Average Class Length                           9
        Minimum Class Length                         0
        Maximum Class Length                        71
      Average Method Length                          1
        Minimum Method Length                        1
        Maximum Method Length                       10
      Average Methods Per Class                      4
        Minimum Methods Per Class                    0
        Maximum Methods Per Class                   49
    Functions                                        0 (0.00%)
      Average Function Length                        0
    Not in classes or functions                      0 (0.00%)

Cyclomatic Complexity
  Average Complexity per LLOC                     0.19
  Average Complexity per Class                    2.84
    Minimum Class Complexity                      1.00
    Maximum Class Complexity                     32.00
  Average Complexity per Method                   1.46
    Minimum Method Complexity                     1.00
    Maximum Method Complexity                     9.00

Dependencies
  Global Accesses                                    0
    Global Constants                                 0 (0.00%)
    Global Variables                                 0 (0.00%)
    Super-Global Variables                           0 (0.00%)
  Attribute Accesses                               229
    Non-Static                                     229 (100.00%)
    Static                                           0 (0.00%)
  Method Calls                                     846
    Non-Static                                     552 (65.25%)
    Static                                         294 (34.75%)

Structure
  Namespaces                                        25
  Interfaces                                         6
  Traits                                            10
  Classes                                          129
    Abstract Classes                                21 (16.28%)
    Concrete Classes                               108 (83.72%)
      Final Classes                                  0 (0.00%)
      Non-Final Classes                            108 (100.00%)
  Methods                                          617
    Scope
      Non-Static Methods                           519 (84.12%)
      Static Methods                                98 (15.88%)
    Visibility
      Public Methods                               504 (81.69%)
      Protected Methods                             98 (15.88%)
      Private Methods                               15 (2.43%)
  Functions                                          2
    Named Functions                                  0 (0.00%)
    Anonymous Functions                              2 (100.00%)
  Constants                                        201
    Global Constants                                 0 (0.00%)
    Class Constants                                201 (100.00%)
      Public Constants                             201 (100.00%)
      Non-Public Constants                           0 (0.00%)

参考

Tags: php, jwt

CodeIgniter 4.3 最速マスター

CodeIgniter 4に関する情報は 「CodeIgniter 4 まとめ」 を参照してください。

(最終更新:2023/01/27)

インストール&設定

インストール

📕インストール

Composerでインストールすると簡単です。

$ composer create-project codeigniter4/appstarter {フォルダ名}

フォルダ構成

CodeIgniter 4.3 のフォルダ構成は以下のようになっています。

ci4app/
├── app/ ... アプリケーション
│   ├── Common.php
│   ├── Cells/       ... (ビューセル)
│   ├── Commands/    ... (コマンド)
│   ├── Config/      ... 設定
│   ├── Controllers/ ... コントローラ
│   ├── Database/    ... データベース
│   ├── Entities/    ... (エンティティ)
│   ├── Filters/     ... コントローラフィルタ
│   ├── Helpers/     ... ヘルパー
│   ├── Language/    ... 言語ファイル
│   ├── Libraries/   ... ライブラリ 
│   ├── Models/      ... モデル
│   ├── ThirdParty/  ... サードパーティライブラリ用
│   ├── Validation/  ... (バリデーションルール)
│   └── Views/       ... ビュー
├── builds*       ... buildsコマンド
├── composer.json
├── composer.lock
├── env           ... 環境変数設定ファイルのサンプル
├── phpunit.xml.dist
├── public/ ... Web公開領域(ドキュメントルート)
│   ├── favicon.ico
│   ├── index.php
│   └── robots.txt
├── spark* ... sparkコマンド
├── tests/ ... テストファイル
│   ├── _support/
│   ├── database/
│   ├── session/
│   └── unit/
├── vendor/   ... Composer管理
└── writable/ ... 書き込み用フォルダ
    ├── cache/    ... キャッシュファイル
    ├── debugbar/ ... デバッグバー用
    ├── logs/     ... ログファイル
    ├── session/  ... セッションファイル
    └── uploads/  ... アップロードファイル

システムメッセージの翻訳のインストール

Composerで最新の開発版の翻訳をインストールします。

$ composer require codeigniter4/translations:dev-develop

設定

📕設定

設定ファイル

app/Config/ フォルダに設定ファイルがあります。設定ファイルはクラスです。 本番環境と共通の設定項目は、設定ファイルを変更します。

特に設定を変更しなくてもCodeIgniterは動作しますが、app/Config/App.php の以下の項目は多くの場合、変更することになるでしょう。

  • $indexPage ... URLにindex.phpを付けない場合は、この設定を空文字にする
  • $defaultLocale ... デフォルトのロケール('ja' に変更すると日本語のシステムメッセージが使用されます)
  • $supportedLocales ... サポートするロケール(優先順位が高い順に記載する)
  • $appTimezone ... タイムゾーン

開発環境固有の設定項目(データベースのパスワードなど)は、env ファイルを .env にコピーして、そこに設定します。

設定クラスのプロパティに対応する環境変数があると、設定ファイルのインスタンス化時に環境変数の値が自動的に設定されます。

複数の環境

📕複数の環境の処理

デフォルトでは、CodeIgniterは本番環境(production)用に動作します。 エラーが発生してもページには表示されません。

開発環境では development を指定します。これで開発モードになり、詳細なエラーやデバッグツールバーが表示されます。 .env で設定できます。

CI_ENVIRONMENT = development

なお、testing はPHPUnitでのテストのための特別な環境です。通常の開発やステージング環境には使えません。

データベース設定

📕データベース設定

app/Config/Database.php に設定クラスがあります。

設定クラスの値を変更したい開発環境固有の設定項目は、以下のように .env に設定します。

.env

database.default.hostname = localhost
database.default.database = ci4app
database.default.username = dbuser
database.default.password = dbpassword

サーバの起動

spark コマンドでPHPビルトインサーバーが起動します。

$ php spark serve

http://localhost:8080/ にアクセスしてください。

ルーティング

📕URIルーティング

CodeIgniter v4.2.0から、セキュリティ上の理由で自動ルーティングはデフォルトでオフになりました。

セキュリティ上の理由から、自動ルーティング(レガシー)は推奨しません。 できる限り、自動ルーティング(レガシー)は使用せず、「手動ルーティングのみ」または「自動ルーティング(改善)」を使いましょう。

手動ルーティング

HTTPメソッドでのルーティング

app/Config/Routes.php にルートを設定します。

GETメソッドの場合は、$routes->get() を使用します。

use App\Controllers\News;

$routes->get('news', [News::class, 'index']);

上記は、http://localhost:8080/news にブラウザからアクセスすると、News コントローラの index() メソッドが呼び出されます。

POSTメソッドの場合は、$routes->post() を使用します。

$routes->post('news/create', [News::class, 'create']);

上記は、http://localhost:8080/news/create にPOSTすると、News コントローラの create() メソッドが呼び出されます。

URLの一部をキャプチャ

(:segment)(:any) などのプレースホルダーを使います。 (:segment) は1つのURIセグメント、(:any) は任意の文字列にマッチします。

$routes->get('news/(:segment)', [News::class, 'view']);

上記は、/news/foo の場合に、News コントローラの view() メソッドに foo を渡します。

なお、上記は、以下のように書くこともできます。

$routes->get('news/(:segment)', '\App\Controllers\News::view/$1');
グループ化

ルートをグループ化することもできます。

$routes->group('admin', function ($routes) {
    $routes->get('users', [\App\Controllers\Admin\Users::class, 'index']);
    $routes->get('blog', [\App\Controllers\Admin\Blog::class, 'index']);
});

上記は、admin/usersadmin/blog のルートを設定しています。

ルートの確認

spark routes コマンドでルートを確認できます。上に表示されたルートが優先されます。

$ php spark routes

自動ルーティング(改善)

📕自動ルーティング(改善)

CodeIgniter v4.1.x までデフォルトだった自動ルーティングは、あまりにも簡単に脆弱性を作り込んでしまうため、 v4.2.0 からは、より安全な「自動ルーティング(改善)」が追加されました。

自動ルーティング(改善)を有効にする

自動ルーティング(改善)を有効にするには、app/Config/Routes.phpapp/Config/Feature.php の 2箇所で設定する必要があります。

app/Config/Routes.php

$routes->setAutoRoute(true);

app/Config/Feature.php

    public bool $autoRoutesImproved = true;

$autoRoutesImprovedfalse だと、自動ルーティング(レガシー)になります。

自動ルーティング(改善)の基本

基本的に以下の規約で自動的にルーティングします。

http://localhost:8080/{コントローラ名}/{メソッド名(HTTP動詞を除く)}/{引数1}/{引数2}/...

以下の Newsコントローラを作成します。

<?php

namespace App\Controllers;

class News extends BaseController
{
    public function getTest($p1 = 'x', $p2 = 'x')
    {
        return '$p1: ' . esc($p1) . ', $p2: ' . esc($p2);
    }
}

すると、以下のルートが自動的に定義されます。

+-----------+---------------------+------------------------------------------+----------------+---------------+
| Method    | Route               | Handler                                  | Before Filters | After Filters |
+-----------+---------------------+------------------------------------------+----------------+---------------+
| GET(auto) | news/test[/..][/..] | \App\Controllers\News::getTest           |                | toolbar       |
+-----------+---------------------+------------------------------------------+----------------+---------------+

getTest()メソッドの最初の get はHTTPメソッド(HTTP動詞)です。この場合はGETメソッドでこのルートにアクセスできることになります。

例えば、POSTメソッドでアクセスさせたい場合は、postTest()メソッドを定義する必要があります。

php spark serve コマンドを実行し、 http://localhost:8080/news/test/a/b にブラウザからアクセスすると、以下が表示されます。

$p1: a, $p2: b 

http://localhost:8080/news/test/a にアクセスすると、以下が表示されます。

$p1: a, $p2: x

http://localhost:8080/news/test にアクセスすると、以下が表示されます。

$p1: x, $p2: x

http://localhost:8080/news/test/a/b/c にアクセスすると、404 - File Not Found が表示されます。 引数の数が合わないからです。

デフォルトメソッド

http://localhost:8080/news でアクセス可能にするには、以下のように getIndex() メソッドを追加します。

<?php

namespace App\Controllers;

class News extends BaseController
{
    // ...

    public function getIndex()
    {
        return __METHOD__;
    }
}

これで http://localhost:8080/news にアクセスすると、以下が表示されます。

App\Controllers\News::getIndex

このようにURLの {メソッド名(HTTP動詞を除く)} の部分を省略したときに実行されるメソッドを「デフォルトメソッド」と言います。 デフォルトでは index が設定されています。

デフォルトコントローラ

http://localhost:8080/ のように {コントローラ名} がないURLにアクセス可能にするには、以下のように Homeコントローラを作成します。

<?php

namespace App\Controllers;

class Home extends BaseController
{
    public function getIndex()
    {
        return view('welcome_message');
    }
}

しかし、デフォルトでは app/Config/Routes.php/ へのルート(以下)が定義されています。

$routes->get('/', 'Home::index');

定義済みのルートは自動ルーティングより優先されるので、Home::index()メソッドが存在しないため、404 - File Not Found になります。

そこで、上記のルートの定義をコメントアウトしてください。

//$routes->get('/', 'Home::index');

これで、http://localhost:8080/ に自動ルーティング(改善)によりアクセス可能になります。

自動ルーティング(改善)の規約
  • URLは基本的にはすべて小文字を想定
  • URLでの {コントローラ名} はすべて小文字、実際のクラス名は先頭のみ大文字
    • サブフォルダを使う場合は、サブフォルダ名も先頭のみ大文字
  • メソッド名には getIndex()postCreate() のようにHTTP動詞のプリフィックスが必要
    • http://localhost:8080/news/create にGETメソッドでリクエストした場合、NewsコントローラのgetCreate()メソッドが実行される
  • メソッド名が省略されたURLの場合、デフォルトメソッド(デフォルトではindex)があれば実行される
    • http://localhost:8080/news にGETメソッドでリクエストした場合、NewsコントローラのgetIndex()メソッドが実行される
  • トップページへのリクエストの場合、デフォルトコントローラ(デフォルトでは Home)が実行される
    • http://localhost:8080/ にGETメソッドでリクエストした場合、HomeコントローラのgetIndex()メソッドが実行される
  • デフォルトコントローラとデフォルトメソッドはURLの中で必ず省略する必要がある
    • http://localhost:8080/ にはアクセス可能だが http://localhost:8080/home には 404 が返る
    • http://localhost:8080/news にはアクセス可能だが http://localhost:8080/news/index には 404 が返る
  • メソッドのパラメータ数がチェックされる
    • パラメータ数が一致しない場合は 404 が返る
  • 手動ルーティングで定義されているコントローラには一切アクセスできない
    • 手動ルーティングするコントローラと自動ルーティングするコントローラは厳密に区別される
    • News::index() へのルートが手動ルーティングで設定されている場合、News::getCreate() メソッドを追加してnews/create にアクセスしても 404 が返る
  • _remap() メソッドはサポートされない

コントローラ

📕コントローラー

コントローラの作成

sparkコマンドで作成できます。

$ php spark make:controller {コントローラクラス名}

以下のようなファイルが作成されます。

app/Controllers/Blog.php

<?php

namespace App\Controllers;

use App\Controllers\BaseController;

class Blog extends BaseController
{
    public function index()
    {
        //
    }
}

コントローラは、BaseController を継承します。 $this->request(Requestオブジェクト) と $this->response(Responseオブジェクト)が使えます。

リクエストパラメータの取得

📕入力の取得

$this->request->getPost()$this->request->getGet()$this->request->getJsonVar() を使います。

$this->request->getPost('title')

上記は、POSTされた title の値を取得します。

バリデーション

📕$this->validateData()

$this->validateData() を使います。

$data = [
    'title' => $this->request->getPost('title'),
    'body'  => $this->request->getPost('body'),
];

$rules = [
    'title' => ['label' => 'タイトル', 'rules' => ['required', 'max_length[100]']],
    'body'  => ['label' => '本文', 'rules' => 'required'],
];

if ($this->request->is('post') && $this->validateData($data, $rules)) {
    // 検証パス
} else {
    // 検証エラー
}

CodeIgniter v4.3.0から、$this->request->is() メソッドでHTTPメソッドを判定できます。

エラーメッセージの上書き

$this->validateData() の第3引数にエラーメッセージを指定します。

if ($this->request->is('post') && $this->validateData(
    $data,
    $rules,
    [
        'title' => [
            'required' => 'タイトルは必須です。',
        ],
    ]
)) {
    // 検証パス
}
独自の検証ルールの作成

📕カスタムルールの作成

app/Config/Validation.php$ruleSets に検証ルールを実装したクラス名を追加します。

public $ruleSets = [
    Rules::class,
    FormatRules::class,
    FileRules::class,
    CreditCardRules::class,
    \App\Libraries\Validation\MyRules::class,
];

検証ルールを実装したクラスは以下のようになり、バリデーションルール even が使えるようになります。

namespace App\Libraries\Validation;

class MyRules
{
    public function even($str, string &$error = null): bool
    {
        if ((int) $str % 2 !== 0) {
            $error = 'エラーメッセージ。';

            return false;
        }

        return true;
    }
}
クロージャルールの作成

CodeIgniter v4.3.0から、一箇所でしか使わないルールはクロージャで定義することもできます。

$rules = [
    'foo' => [
        'required',
        static function ($value, $data, &$error, $field) {
            if ((int) $value % 2 === 0) {
                return true;
            }

            $error = 'エラーメッセージ。';

            return false;
        },
    ],
];

if ($this->request->is('post') && $this->validateData($data, $rules)) {
    // 検証パス
}

モデルの呼び出し

📕model() 関数を使います。

$model = model(UserModel::class);

ビューのレンダリング

📕view() 関数を使います。

return view('pages/view_filename', $data);

第1引数はビューファイル名、第2引数はビューに渡すデータの配列です。 ビューファイル名、app/Views/ 以下のファイルパスを .php を除いて指定します。

ログの出力

📕log_message() 関数を使います。

log_message('debug', 'デバッグ用のログメッセージ。');

ログファイルは、writable/logs/ に日付別に作成されます。

リダイレクト

📕redirect() 関数を使います。

return redirect()->to('home/index');

リダイレクトさせるには、RedirectResponse オブジェクトを返す必要があります。

API

📕RESTfulなリソース処理

ルーティング

app/Config/Routes.php

$routes->resource('photos');

上記を設定すると、以下のルートが設定されたことになります。

$routes->get('photos/new',             'Photos::new');
$routes->post('photos',                'Photos::create');
$routes->get('photos',                 'Photos::index');
$routes->get('photos/(:segment)',      'Photos::show/$1');
$routes->get('photos/(:segment)/edit', 'Photos::edit/$1');
$routes->put('photos/(:segment)',      'Photos::update/$1');
$routes->patch('photos/(:segment)',    'Photos::update/$1');
$routes->delete('photos/(:segment)',   'Photos::delete/$1');

不要なメソッドがあれば、除外指定します。

$routes->resource('photos', ['except' => 'new,edit']);

コントローラ

ResourceController を継承します。

<?php

namespace App\Controllers;

use App\Models\PhotoModel;
use CodeIgniter\RESTful\ResourceController;

class Photos extends ResourceController
{
    protected $modelName = PhotoModel::class;
    protected $format    = 'json';

    public function index()
    {
        return $this->respond($this->model->findAll());
    }

    // ...
}

ResponseTrait に実装されている $this->respond() などのメソッドが使えるようになります。 上記のコードでは、モデルを検索した結果がJSONで返されます。

ビュー

📕ビュー

デフォルトでは、ビューファイルは普通のPHPファイルです。

個人的には、出力時に自動でエスケープ処理してくれるTwigなどのテンプレートエンジンかView Parserの使用を推奨します。

デフォルトのView

コントローラから渡された配列が、 キーを変数名とした変数に設定されます。

変数を出力する場合は、忘れずに esc() 関数でエスケープします。

View Parser

📕ビューパーサー

テンプレートエンジンです。変数の値の自動HTMLエスケープフィルター機能も提供します。

コントローラから渡された配列の値が {blog_title} のような疑似変数に表示されます。 値は自動的にHTMLエスケープされます。

{ } はJavaScriptと相性が悪いので、設定で {{ }} に変更することをお薦めします。

モデル

CodeIgniter\Model が標準で提供されていますが、使わなくても構いません。 データベース操作が必要な場合、 最小限のモデルクラスは以下のようになります。

app/Models/UserModel.php

<?php

namespace App\Models;

use CodeIgniter\Database\ConnectionInterface;

class UserModel
{
    protected $db;

    public function __construct(ConnectionInterface $db)
    {
        $this->db = $db;
    }
}

$db はインスタンス化する時に自分で注入してください。

モデルの作成

以下のようなファイルを作成します。

app/Models/UserModel.php

<?php

namespace App\Models;

use CodeIgniter\Model;

class UserModel extends Model
{
    protected $table = 'users';
}

CodeIgniter\Model を継承すると、いろいろ便利な機能が使えます。

クエリビルダー

📕クエリビルダークラス

ビルダーの生成

db_connect() 関数で「データベース接続」オブジェクトを取得できます。

$db = db_connect();

データベース接続オブジェクトの table() メソッドにテーブル名を指定すると、新しいクエリビルダーを取得できます。

$builder = $db->table('users');
SELECT
$query = $builder->where('name !=', $name)
    ->where('id <', $id)
    ->orderBy('name', 'ASC')
    ->get();

foreach ($query->getResult() as $row) {
    echo $row->title;
}

CodeIgniter3との違いは、テーブル名をビルダー生成時に指定することとと、メソッド名がcamelCaseに変わった程度です。

INSERT

insert() メソッドを使います。

$data = [
    'title' => 'My title',
    'name'  => 'My Name',
    'date'  => 'My date',
];
$builder->insert($data);
UPDATE

update() メソッドを使います。

$data = [
    'title' => $title,
    'name'  => $name,
    'date'  => $date,
];
$builder->where('id', $id);
$builder->update($data);
DELETE

delete() メソッドを使います。

$builder->delete(['id' => $id]);

where() メソッドでレコードを指定することもできます。

$builder->where('id', $id);
$builder->delete();

CodeIgniter\Model

📕CodeIgniterのモデルの使用

以下のような機能があります。

  • 自動データベース接続
  • 基本的なCRUDメソッド
  • 自動ページネーション
  • 論理削除
データの検索

📕データの検索

find()

プライマリーキーでレコードを検索します。

$user  = $userModel->find($id);
$users = $userModel->find([1, 2, 3]);
findAll()

レコードを検索します。

$users = $userModel->findAll();
$users = $userModel->where('active', 1)->findAll();
$users = $userModel->findAll($limit, $offset);
データの保存

📕データの保存

insert()

レコードを挿入します。

$data = [
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->insert($data);
update()

プライマリーキーを指定してレコードを更新します。

$data = [
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->update($id, $data);
$data = [
    'active' => 1,
];
$userModel->update([1, 2, 3], $data);

WHERE句を指定してレコードを更新します。

$userModel->whereIn('id', [1, 2, 3])->set(['active' => 1])->update();
save()

プライマリーキーがない場合は挿入します。

$data = [
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->save($data);

プライマリーキーがある場合は更新します。

$data = [
    'id'       => 3,
    'username' => 'darth',
    'email'    => 'd.vader@example.com',
];
$userModel->save($data);
データの削除

📕データの削除

delete()

プライマリーキーを指定してレコードを削除します。

$userModel->delete(12);
$userModel->delete([1, 2, 3]);

WHERE句を指定してレコードを削除します。

$userModel->where('id', 12)->delete();

Entityクラス

📕エンティティクラスの使用

テーブルのレコードを表すクラスです。POPOではありません。使わなくても構いません。

app/Entities/User.php

<?php

namespace App\Entities;

use CodeIgniter\Entity\Entity;

class User extends Entity
{
    // ...
}

CodeIgniter\Model では、$returnType プロパティに指定すると、検索結果がこのクラスのインスタンスになります。

    protected $returnType = \App\Entities\User::class;

クエリビルダーでも getResult() メソッドで指定すれば使えます。

$query->getResult(\App\Entities\User::class);

認証

ユーザガイドの 📕認証 に推奨事項が記載されています。

執筆時点で上記を満たす推奨できるパッケージは codeigniter4/shieldmyth/auth 以外ありません。基本的に公式パッケージである codeigniter4/shield を使うことを推奨します。

セッション

📕セッションライブラリ

以下でセッションオブジェクトが取得できます。

$session = session();

セッションの読み書き

書き込み

1アイテムを書き込む場合。

$session->set('some_name', 'some_value');

まとめて書き込む場合。

$newdata = [
    'username'  => 'johndoe',
    'email'     => 'johndoe@example.com',
    'logged_in' => true,
];
$session->set($newdata);
読み込み
$session->get('some_name');
削除

1アイテムを削除する場合。

$session->remove('some_name');

まとめて削除する場合。

$items = ['username', 'email'];
$session->remove($items);

参考

Tags: codeigniter, codeigniter4

CodeIgniter 4.3 の変更点

CodeIgniter 4.3 は 2023-01-10 にリリースされたCodeIgniter4の最新バージョンです。

多くの機能追加と 破壊的な変更 が含まれています。アップグレードする前に以下を必ず確認してください。

ハイライト

  • Query Builderに upsert() および upsertBatch()deleteBatch() メソッドが追加され、また、*Batch() メソッドはクエリからデータをセットできるようになりました。
  • デフォルトのバリデーションルールがStrictルールに変更されました。
  • データベースエラーが発生した場合の例外処理が変更されました。

主な破壊的変更

データベースエラーが発生した場合の例外処理

データベースエラーが発生した場合に例外を投げるかどうかが変更されました。

以前のバージョンでは、開発・テスト環境では、データベースエラーが発生した場合に例外が投げられました。しかし、本番環境では例外は投げられず false が返りました。

このように本番環境と開発・テスト環境で振る舞いが変わると、テストが書きづらいです。また、開発環境での動作しか知らない場合は、返り値をチェックするコードを書き忘れ、本番環境でのみエラーが発生しても処理が続いてしまう可能性がありました。

そのため、以下のように変更されました。

  • データベースエラーが発生したときに投げられる例外クラスが、データベースドライバにより異なっていたものが、DatabaseException に統一されました。
  • DBDebugCI_DEBUG の変更
    • データベース設定ファイルでのデフォルトの DBDebug の値が true に変更されました。
    • 以前のバージョンでは、本番環境にみ false に設定されていました。
    • Query Builderは DBDebugtrue の場合に例外を投げるようになりました。
    • 以前のバージョンでは、CI_DEBUGtrue の場合に例外を投げていました。
    • 上記に変更により、DBDebug はエラー時に例外を投げるかどうかのフラグに意味が変わりました。
    • ただし、トランザクション中は、DBDebugtrue でも例外が発生しないように変更されました。
    • もし、トランザクション中でも例外が発生して欲しい場合は、$db->transException(true) をトランザクション開始前に実行してください。

これで、開発時でも本番環境でも、エラーが発生した場合は例外が発生するようになりました(トランザクション中は除く)。

ただし、データベース設定ファイルは composer update では更新されませんので、既存のプロジェクトはほぼ今まで通り、本番では例外が発生しないままになります。

主な機能追加

Formヘルパー

バリデーションのエラーを表示するための validation_errors()validation_list_errors()validation_show_error() 関数が追加されました。

Query Builder

upsert()upsertBatch()deleteBatch() メソッドが追加されました。

また、クエリーの結果をデータとして使う setQueryAsData() メソッドが追加され、insertBatch()updateBatch()upsertBatch()deleteBatch() メソッドで使えます。例えば、SELECTした結果をINSERTすることができます。

$subQuery = $this->db->table('user2')
            ->select('user2.name, user2.email, user2.country')
            ->join('user', 'user.email = user2.email', 'left')
            ->where('user.email IS NULL');
$this->db->table('user')->setQueryAsData($subQuery)->insertBatch();

Database Forge

既存のテーブルにインデックスを追加する Forge::processIndexes() メソッドが追加されました。

$this->forge->addKey(['category', 'name'], false, false, 'category_name');
$this->forge->addPrimaryKey('id', 'pk_actions');
$this->forge->addForeignKey('userid', 'user', 'id', '', '', 'userid_fk');
$this->forge->processIndexes('actions');

複数ドメインのサポート

設定ファイル app/Config/App.php$allowedHostnames が追加され、$baseURL で設定されているホスト名以外の別のホスト名を設定することができるようになりました。

この設定により、 base_url()current_url()site_url() などの返す値のホスト名部分が、リクエストに応じて変わります。

spark routes コマンド

spark routes コマンドが、ルート名を表示するようになりました。

$ php spark routes

CodeIgniter v4.3.0 Command Line Tool - Server Time: 2023-01-13 08:42:59 UTC+00:00

+-----------+-----------------+------+-----------------------------------+----------------+---------------+
| Method    | Route           | Name | Handler                           | Before Filters | After Filters |
+-----------+-----------------+------+-----------------------------------+----------------+---------------+
| GET       | /               | »    | \App\Controllers\Home::index      |                | toolbar       |
| GET       | feed            | »    | (Closure)                         |                | toolbar       |
| GET(auto) | product/list/.. |      | \App\Controllers\Product::getList |                | toolbar       |
+-----------+-----------------+------+-----------------------------------+----------------+---------------+

»Route と同じことを意味します。ルート feed のルート名は feed ということです。 なお、自動ルートにはルート名はありません。

HTML5互換性の向上

設定ファイル app/Config/DocTypes.php$html5 が追加され、true にセットされた場合は、ヘルパーなどが出力するHTMLの空の要素(brなど)が <br /> ではなく、HTML5互換の <br> になります。

他にも機能追加は多数あります。全てのリストは、 ChageLog を参照してください。

関連

参考

Tags: codeigniter, codeigniter4, release