diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 00000000..1dfeb762 --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,119 @@ +#!/bin/bash + +set -o pipefail + +GIT_ROOT=$(git rev-parse --show-toplevel) # ".../lti-1-3-php-library" + +# So we avoid the "Not a git repository" error when performing git commands in a subdir +unset GIT_DIR + +# Get changed files to be committed, excluding deleted files (since we can't grep them) +CHANGED_FILES=$(git diff --cached --name-only --diff-filter=d) + +NO_FORMAT="\e[0m" +F_BOLD="\e[1m" +C_RED="\e[31m" +C_YELLOW="\e[93m" +C_CYAN="\e[36m" +C_LIME="\e[92m" + +# Rather than doing `return 1`, we fail fast. +function fail { + echo -e "${C_RED}${F_BOLD}Pre-commit hook failed! Fix the above errors before committing.${NO_FORMAT}" + exit 1 +} + +function file_ends_with_newline { + file_path=$1 + + # NOTE: Empty files technically end with a newline. + [[ $(wc -l < "$file_path") -eq 0 ]] || [[ $(tail -c1 "$file_path" | wc -l) -gt 0 ]] +} + +function is_executable_installed { + executable_name=$1 + + which "$executable_name" >/dev/null +} + +# Returns a 0 status code if the given feature is enabled, 1 otherwise. +# Feature names are arbitrarily defined in the optional file `.skipped-checks` +# in order to give more control to developers to-as what gets executed. +function feature_is_enabled { + feature_name=$1 + + # We redirect output so that it doesn't emit warnings if the file doesn't exist. + ! grep "$feature_name" "$GIT_ROOT/.githooks/.skipped-checks" &> /dev/null +} + +function feature_is_disabled { + feature_name=$1 + + ! feature_is_enabled "$feature_name" +} + +function skip_if_no_changes { + if [[ -z "$CHANGED_FILES" ]]; then + echo "No changes were detected while running the pre-commit hook." && exit 0 + fi +} + +function skip_if_merge_in_progress { + if [ -f ".git/MERGE_HEAD" ]; then + echo "Detected merge in progress, skipping pre-commit hook." && exit 0 + fi +} + +function fail_if_unresolved_merge_conflict { + # Check the files to prevent merge markers from being committed. + if echo "$CHANGED_FILES" | xargs --no-run-if-empty egrep '[><]{7}' -H -I --line-number; then + echo -e "${C_RED}You have merge markers (conflicts) in the above files, lines. Fix them before committing.${NO_FORMAT}" && fail + fi +} + +function lint_eof_newlines { + if feature_is_disabled "pre-commit-auto-newlines"; then + return 0 + fi + + text_files=$(echo "$CHANGED_FILES" | grep -E '\.(css|docker|Dockerfile|dockerignore|ejs|env|example|gitignore|html|js|json|php|py|rb|scss|sh|svg|toml|trivyignore|ts|txt|yaml|yml)$') + for f in $text_files; do + # Add a linebreak to the file if it doesn't have one + if ! file_ends_with_newline "$f"; then + echo >>"$f" + git add "$f" + fi + done +} + +function lint_php { + php_files=$(echo "$CHANGED_FILES" | grep '\.php') + if [[ -z "$php_files" ]]; then + return 0 # There's nothing to lint. + fi + + phpcsfixer="vendor/bin/php-cs-fixer" + + if ! [ -x "$phpcsfixer" ]; then + echo -e "${C_RED}PHP-CS-Fixer is not installed. Install it with \`composer install\`.${NO_FORMAT}" && return 1 + fi + + php_files_arg=$(echo "$php_files" | tr '\n' ' ') + + echo -e "${C_CYAN}Linting PHP-CS-Fixer...${NO_FORMAT}" + $phpcsfixer fix -q || return 1 + + git add $php_files_arg +} + +echo -e "${NO_FORMAT}${F_BOLD}Running pre-commit hook...${NO_FORMAT}" + +skip_if_merge_in_progress +skip_if_no_changes + +fail_if_unresolved_merge_conflict + +lint_eof_newlines || fail +lint_php || fail + +echo -e "${C_LIME}${F_BOLD}Pre-commit hook passed!${NO_FORMAT}" diff --git a/README.md b/README.md index f95f6bbd..aa71587d 100644 --- a/README.md +++ b/README.md @@ -44,11 +44,11 @@ A JWKS (JSON Web Key Set) endpoint can be generated for either an individual reg use Packback\Lti1p3\JwksEndpoint; // From issuer -JwksEndpoint::fromIssuer($database, 'http://example.com')->outputJwks(); +JwksEndpoint::fromIssuer($database, 'http://example.com')->getPublicJwks(); // From registration -JwksEndpoint::fromRegistration($registration)->outputJwks(); +JwksEndpoint::fromRegistration($registration)->getPublicJwks(); // From array -JwksEndpoint::new(['a_unique_KID' => file_get_contents('/path/to/private/key.pem')])->outputJwks(); +JwksEndpoint::new(['a_unique_KID' => file_get_contents('/path/to/private/key.pem')])->getPublicJwks(); ``` ## Documentation diff --git a/src/ImsStorage/ImsCache.php b/src/ImsStorage/ImsCache.php index 41f7bc48..a0be843c 100644 --- a/src/ImsStorage/ImsCache.php +++ b/src/ImsStorage/ImsCache.php @@ -5,7 +5,7 @@ use Packback\Lti1p3\Interfaces\ICache; /** - * @todo Deprecate this in the next major version + * @deprecated */ class ImsCache implements ICache { diff --git a/src/ImsStorage/ImsCookie.php b/src/ImsStorage/ImsCookie.php index dab75f2e..3e3b552c 100644 --- a/src/ImsStorage/ImsCookie.php +++ b/src/ImsStorage/ImsCookie.php @@ -5,7 +5,7 @@ use Packback\Lti1p3\Interfaces\ICookie; /** - * @todo Deprecate this in the next major version + * @deprecated */ class ImsCookie implements ICookie { diff --git a/src/JwksEndpoint.php b/src/JwksEndpoint.php index 4aa8a423..92197f17 100644 --- a/src/JwksEndpoint.php +++ b/src/JwksEndpoint.php @@ -6,6 +6,9 @@ use Packback\Lti1p3\Interfaces\ILtiRegistration; use phpseclib3\Crypt\RSA; +/** + * @todo Pin versions to v6.6 and php 8 + */ class JwksEndpoint { private $keys; @@ -49,10 +52,12 @@ public function getPublicJwks() } /** - * @todo: Deprecate this in the next major version + * @deprecated */ public function outputJwks() { + trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED); + echo json_encode($this->getPublicJwks()); } } diff --git a/src/LtiDeepLink.php b/src/LtiDeepLink.php index ba6764c9..e1ce2d8e 100644 --- a/src/LtiDeepLink.php +++ b/src/LtiDeepLink.php @@ -44,18 +44,12 @@ public function getResponseJwt($resources) } /** - * This method builds an auto-submitting HTML form to post the deep linking response message - * back to platform, as per LTI-DL 2.0 specification. The resulting HTML is then written to standard output, - * so calling this method will automatically send an HTTP response to conclude the content selection flow. - * - * @param LtiDeepLinkResource[] $resources The list of selected resources to be sent to the platform - * - * @todo Consider wrapping the content inside a well-formed HTML document, - * and returning it instead of directly writing to standard output - * @todo Deprecate this in the next major version + * @deprecated */ public function outputResponseForm($resources) { + trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED); + $jwt = $this->getResponseJwt($resources); $formActionUrl = $this->deep_link_settings['deep_link_return_url']; diff --git a/src/LtiDeepLinkResource.php b/src/LtiDeepLinkResource.php index 093f979c..5f4ba7a8 100644 --- a/src/LtiDeepLinkResource.php +++ b/src/LtiDeepLinkResource.php @@ -125,6 +125,8 @@ public function setCustomParams(array $value): LtiDeepLinkResource */ public function getTarget(): string { + trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED); + return $this->target; } @@ -134,6 +136,8 @@ public function getTarget(): string */ public function setTarget(string $value): LtiDeepLinkResource { + trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED); + $this->target = $value; return $this; diff --git a/src/LtiMessageLaunch.php b/src/LtiMessageLaunch.php index b3ad2825..2f15c69e 100644 --- a/src/LtiMessageLaunch.php +++ b/src/LtiMessageLaunch.php @@ -121,7 +121,7 @@ public static function fromCache( ICache $cache = null, ILtiServiceConnector $serviceConnector = null ) { - // @TODO: Fix the null here on the next major version + // @todo: Fix the null here on the next major version $new = new LtiMessageLaunch($database, $cache, null, $serviceConnector); $new->launch_id = $launch_id; $new->jwt = ['body' => $new->cache->getLaunchData($launch_id)]; @@ -171,7 +171,7 @@ public function validate(array $request = null) ->validateJwtSignature() ->validateDeployment() ->validateMessage() - // @TODO: Remove this in v6.0 + // @todo remove this in v6.0 ->cacheLaunchData(); } diff --git a/src/LtiOidcLogin.php b/src/LtiOidcLogin.php index c77f1d0b..f6582107 100644 --- a/src/LtiOidcLogin.php +++ b/src/LtiOidcLogin.php @@ -48,6 +48,7 @@ public static function new(IDatabase $database, ICache $cache = null, ICookie $c */ public function doOidcLoginRedirect($launchUrl, array $request = null) { + // @todo remove this in v6.0 if ($request === null) { $request = $_REQUEST; } diff --git a/src/Redirect.php b/src/Redirect.php index a87ed5f1..f90f6961 100644 --- a/src/Redirect.php +++ b/src/Redirect.php @@ -22,8 +22,12 @@ public function doRedirect() exit; } + /** + * @deprecated + */ public function doHybridRedirect(ICookie $cookie) { + trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED); if (!empty($cookie->getCookie(self::$CAN_302_COOKIE))) { return $this->doRedirect(); } @@ -36,8 +40,13 @@ public function getRedirectUrl() return $this->location; } + /** + * @deprecated + */ public function doJsRedirect() { + trigger_error('Method '.__METHOD__.' is deprecated', E_USER_DEPRECATED); + ?> If you are not automatically redirected, click here to continue