├── LICENSE ├── README.md ├── composer.json ├── config ├── app.yaml ├── routing.yaml └── services.yaml ├── install ├── Makefile ├── bin │ └── console ├── docker-compose.prod.yml ├── docker-compose.yml ├── docker │ ├── dev │ │ └── php │ │ │ └── config.ini │ ├── dk.nginx.Dockerfile │ ├── dk.nginx.Dockerfile.dockerignore │ ├── dk.php.Dockerfile │ ├── dk.php.Dockerfile.dockerignore │ └── nginx │ │ ├── conf.d │ │ └── default.conf │ │ └── nginx.conf └── wp │ └── wp-config.php └── src ├── Attribute └── AsWordpressCommand.php ├── Command ├── WordpressCommand.php └── WpCommand.php ├── Controller ├── ReauthenticateController.php ├── Routes.php └── WordpressController.php ├── DependencyInjection ├── Compiler │ └── WordpressPass.php ├── Configuration.php └── SwordExtension.php ├── Entity └── WordpressEntityInterface.php ├── EnvVarProcessor └── AutoFileEnvVarProcessor.php ├── Event └── WooCommerceRegistrationEvent.php ├── EventListener ├── WordpressLoggedInStatusCheckEventSubscriber.php ├── WordpressTablePrefixEventListener.php └── WordpressTerminateEventListener.php ├── Exception ├── WordpressLoginSuccessfulException.php └── WordpressLougoutSuccessfulException.php ├── Helper └── ContainerHelper.php ├── Loader ├── LazyServiceInstantiator.php ├── WordpressGlobals.php ├── WordpressLoader.php └── wp-load.php ├── Security ├── User.php ├── UserAuthenticator.php ├── UserProvider.php ├── UserReauthenticator.php └── Voter │ ├── CapabilityVoter.php │ └── RoleVoter.php ├── Service ├── AbstractWordpressService.php └── WordpressService.php ├── Store └── WordpressWidgetStore.php ├── SwordBundle.php ├── Translation ├── ChildThemeTranslationInitializer.php └── LocaleSwitcher.php └── Widget └── DefineWidgetTrait.php /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022-2023, William Arin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Sword Logo](/assets/sword-logo-tagline-sm.png) 2 | 3 |
4 | 5 | [![Github Workflow](https://github.com/phpsword/sword-bundle/workflows/Test/badge.svg)](https://github.com/phpsword/sworld-bundle/actions) 6 | 7 | # Introduction 8 | 9 | Modern WordPress development with Symfony. 10 | 11 | # Documentation 12 | Visit [https://getsword.com/](https://getsword.com/) for official documentation. 13 | 14 | # Contributions 15 | Feel free to submit issues and pull requests. 16 | 17 | # License 18 | [MIT](LICENSE) 19 | 20 | Copyright (c) 2022, William Arin 21 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "phpsword/sword-bundle", 3 | "description": "Modern WordPress development with Symfony", 4 | "license": "MIT", 5 | "type": "symfony-bundle", 6 | "keywords": [ 7 | "wordpress", 8 | "modern-wordpress", 9 | "symfony", 10 | "symfony-bundle" 11 | ], 12 | "authors": [ 13 | { 14 | "name": "William Arin", 15 | "email": "williamarin.dev@gmail.com" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=8.1", 20 | "ext-ctype": "*", 21 | "ext-iconv": "*", 22 | "composer/composer": "^2.4.1", 23 | "doctrine/doctrine-bundle": "^2.7.2", 24 | "doctrine/orm": "^2.13 || ^3.0", 25 | "roots/wp-password-bcrypt": "^1.1.0", 26 | "symfony/dependency-injection": "^6.1 || ^7.0", 27 | "symfony/framework-bundle": "^6.1 || ^7.0", 28 | "symfony/http-kernel": "^6.1 || ^7.0", 29 | "symfony/security-bundle": "^6.1 || ^7.0", 30 | "symfony/translation": "^6.1 || ^7.0", 31 | "symfony/yaml": "^6.1 || ^7.0", 32 | "williarin/wordpress-interop": "^1.11", 33 | "williarin/wordpress-interop-bundle": "^1.1.1", 34 | "wp-cli/wp-cli-bundle": "^2.10" 35 | }, 36 | "require-dev": { 37 | "brain/monkey": "^2.6", 38 | "ergebnis/composer-normalize": "^2.28.3", 39 | "kubawerlos/php-cs-fixer-custom-fixers": "^3.11", 40 | "matthiasnoback/symfony-dependency-injection-test": "^4.3", 41 | "mockery/mockery": "^1.5", 42 | "nikic/php-parser": "^4.15", 43 | "php-parallel-lint/php-parallel-lint": "^1.3", 44 | "phpro/grumphp": "^1.13", 45 | "phpunit/phpunit": "^9.5.22", 46 | "roave/security-advisories": "dev-latest", 47 | "symplify/coding-standard": "^11.1", 48 | "symplify/easy-coding-standard": "^11.1" 49 | }, 50 | "minimum-stability": "dev", 51 | "prefer-stable": true, 52 | "autoload": { 53 | "psr-4": { 54 | "Sword\\SwordBundle\\": "src/" 55 | }, 56 | "files": [ 57 | "src/Helper/ContainerHelper.php" 58 | ] 59 | }, 60 | "autoload-dev": { 61 | "psr-4": { 62 | "Sword\\SwordBundle\\Test\\": "tests/" 63 | } 64 | }, 65 | "config": { 66 | "allow-plugins": { 67 | "ergebnis/composer-normalize": true, 68 | "phpro/grumphp": false 69 | }, 70 | "sort-packages": true 71 | }, 72 | "extra": { 73 | "branch-alias": { 74 | "dev-master": "1.x-dev" 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /config/app.yaml: -------------------------------------------------------------------------------- 1 | security: 2 | password_hashers: 3 | Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' 4 | providers: 5 | app_user_provider: 6 | id: Sword\SwordBundle\Security\UserProvider 7 | role_hierarchy: 8 | customer: ROLE_USER 9 | subscriber: customer 10 | contributor: subscriber 11 | author: contributor 12 | editor: author 13 | shop_manager: editor 14 | administrator: shop_manager 15 | ROLE_SUPER_ADMIN: [ administrator, ROLE_ALLOWED_TO_SWITCH ] 16 | firewalls: 17 | dev: 18 | pattern: ^/(_(profiler|wdt)|css|images|js)/ 19 | security: false 20 | main: 21 | lazy: true 22 | logout: true 23 | provider: app_user_provider 24 | entry_point: Sword\SwordBundle\Security\UserAuthenticator 25 | custom_authenticator: Sword\SwordBundle\Security\UserAuthenticator 26 | access_control: 27 | - { path: ^/, role: PUBLIC_ACCESS } 28 | 29 | doctrine: 30 | dbal: 31 | dbname: '%env(auto_file:WORDPRESS_DB_NAME)%' 32 | host: '%env(auto_file:WORDPRESS_DB_HOST)%' 33 | user: '%env(auto_file:WORDPRESS_DB_USER)%' 34 | password: '%env(auto_file:WORDPRESS_DB_PASSWORD)%' 35 | charset: '%env(auto_file:WORDPRESS_DB_CHARSET)%' 36 | driver: 'mysqli' 37 | server_version: '8.0' 38 | url: ~ 39 | 40 | orm: 41 | auto_generate_proxy_classes: true 42 | naming_strategy: doctrine.orm.naming_strategy.underscore_number_aware 43 | auto_mapping: true 44 | mappings: 45 | SwordSecurity: 46 | is_bundle: false 47 | dir: '%kernel.project_dir%/vendor/phpsword/sword-bundle/src/Security' 48 | prefix: 'Sword\SwordBundle\Security' 49 | alias: SwordSecurity 50 | 51 | framework: 52 | serializer: 53 | name_converter: 'serializer.name_converter.camel_case_to_snake_case' 54 | 55 | williarin_wordpress_interop: 56 | entity_managers: 57 | default: 58 | connection: default 59 | -------------------------------------------------------------------------------- /config/routing.yaml: -------------------------------------------------------------------------------- 1 | controllers: 2 | resource: ../src/Controller/ 3 | type: attribute 4 | -------------------------------------------------------------------------------- /config/services.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | _defaults: 3 | autowire: true # Automatically injects dependencies in your services. 4 | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. 5 | 6 | Sword\SwordBundle\: 7 | resource: '../src/' 8 | exclude: 9 | - '../src/Attribute/' 10 | - '../src/DependencyInjection/' 11 | - '../src/Helper/' 12 | -------------------------------------------------------------------------------- /install/Makefile: -------------------------------------------------------------------------------- 1 | .DEFAULT_GOAL = help 2 | 3 | .PHONY: cc 4 | cc: ## Clear Symfony cache 5 | @docker compose exec php bin/console cache:clear 6 | 7 | .PHONY: opcache 8 | opcache: ## Clear opcache 9 | docker-compose exec php cachetool opcache:reset 10 | 11 | .PHONY: build 12 | build: ## Build containers 13 | @docker compose build 14 | 15 | .PHONY: stop 16 | stop: ## Stop containers 17 | @docker compose stop 18 | 19 | .PHONY: down 20 | down: ## Remove containers but keep volumes 21 | @docker compose down 22 | 23 | .PHONY: remove 24 | remove: ## Remove containers and volumes 25 | @docker compose down --remove-orphans -v 26 | 27 | .PHONY: up 28 | up: ## Run containers 29 | @docker compose up -d --remove-orphans 30 | 31 | .PHONY: up-build 32 | up-build: ## Build containers and run them 33 | @docker compose up -d --build --remove-orphans 34 | 35 | .PHONY: upgrade 36 | upgrade: ## Upgrade database after a WordPress upgrade 37 | @docker-compose exec -u 82:82 php bin/console wp core update-db 38 | @docker-compose exec -u 82:82 php bin/console wp wc update 39 | @docker-compose exec -u 82:82 php bin/console wp cron event run --due-now 40 | @docker-compose exec -u 82:82 php bin/console wp action-scheduler run 41 | 42 | .PHONY: acl 43 | acl: ## Reset project files and directories ACL 44 | @sudo chown -R $(whoami):$(whoami) . 45 | @sudo setfacl -dR -m u:$(whoami):rwX -m u:www-data:rwX -m u:82:rwX . 46 | @sudo setfacl -R -m u:$(whoami):rwX -m u:www-data:rwX -m u:82:rwX . 47 | 48 | .PHONY: help 49 | help: ## List all commands 50 | @grep -E '(^[a-zA-Z_-]+:.*?##.*$$)|(^##)' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[32m%-30s\033[0m %s\n", $$1, $$2}' | sed -e 's/\[32m##/[33m/' 51 | -------------------------------------------------------------------------------- /install/bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | = 2 && $argv[1] === 'wp') { 18 | $bootstrapSteps = [ 19 | DeclareFallbackFunctions::class, 20 | LoadUtilityFunctions::class, 21 | LoadDispatcher::class, 22 | ]; 23 | 24 | if (!defined('WP_CLI')) { 25 | define('WP_CLI', true); 26 | } 27 | if (!defined('WP_CLI_ROOT')) { 28 | define('WP_CLI_ROOT', dirname(__DIR__) . '/vendor/wp-cli/wp-cli'); 29 | } 30 | if (!defined('WP_CLI_VERSION')) { 31 | define('WP_CLI_VERSION', trim(file_get_contents(WP_CLI_ROOT . '/VERSION'))); 32 | } 33 | if (!defined('WP_CLI_START_MICROTIME')) { 34 | define('WP_CLI_START_MICROTIME', microtime(true)); 35 | } 36 | 37 | if (!defined('WP_CLI_VENDOR_DIR')) { 38 | if (file_exists(WP_CLI_ROOT . '/vendor/autoload.php')) { 39 | define('WP_CLI_VENDOR_DIR', WP_CLI_ROOT . '/vendor'); 40 | } elseif (file_exists(dirname(WP_CLI_ROOT, 2) . '/autoload.php')) { 41 | define('WP_CLI_VENDOR_DIR', dirname(WP_CLI_ROOT, 2)); 42 | } elseif (file_exists(dirname(WP_CLI_ROOT) . '/vendor/autoload.php')) { 43 | define('WP_CLI_VENDOR_DIR', dirname(WP_CLI_ROOT) . '/vendor'); 44 | } else { 45 | define('WP_CLI_VENDOR_DIR', WP_CLI_ROOT . '/vendor'); 46 | } 47 | } 48 | 49 | require_once WP_CLI_ROOT . '/php/compat.php'; 50 | 51 | $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0'; 52 | $_SERVER['HTTP_USER_AGENT'] = ''; 53 | $_SERVER['REQUEST_METHOD'] = 'GET'; 54 | $_SERVER['REMOTE_ADDR'] = '127.0.0.1'; 55 | 56 | require_once WP_CLI_ROOT . '/php/WP_CLI/Autoloader.php'; 57 | 58 | $autoloader = new Autoloader(); 59 | 60 | $autoloader->add_namespace( 61 | 'WP_CLI\Bootstrap', 62 | WP_CLI_ROOT . '/php/WP_CLI/Bootstrap' 63 | )->register(); 64 | 65 | global $wpCliBootstrapState; 66 | $wpCliBootstrapState = new BootstrapState(); 67 | 68 | foreach ($bootstrapSteps as $step) { 69 | /** @var BootstrapStep $stepInstance */ 70 | $stepInstance = new $step(); 71 | $wpCliBootstrapState = $stepInstance->process($wpCliBootstrapState); 72 | } 73 | } 74 | 75 | require_once dirname(__DIR__) . '/vendor/autoload_runtime.php'; 76 | 77 | return function (array $context) { 78 | $kernel = new Kernel($context['APP_ENV'], (bool)$context['APP_DEBUG']); 79 | 80 | return new Application($kernel); 81 | }; 82 | -------------------------------------------------------------------------------- /install/docker-compose.prod.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | php: 5 | build: 6 | context: . 7 | dockerfile: docker/dk.php.Dockerfile 8 | args: 9 | PHP_VERSION: 8.1 10 | image: 'changeme/changeme-php:staging' 11 | restart: 'no' 12 | environment: 13 | WORDPRESS_SITE_URL: https://staging.example.com 14 | WORDPRESS_DB_NAME: sword_app 15 | WORDPRESS_DB_USER: ChangeMe 16 | WORDPRESS_DB_PASSWORD: ChangeMe 17 | WORDPRESS_DB_HOST: mysql 18 | WORDPRESS_AUTH_KEY: ChangeMe 19 | WORDPRESS_SECURE_AUTH_KEY: ChangeMe 20 | WORDPRESS_LOGGED_IN_KEY: ChangeMe 21 | WORDPRESS_NONCE_KEY: ChangeMe 22 | WORDPRESS_AUTH_SALT: ChangeMe 23 | WORDPRESS_SECURE_AUTH_SALT: ChangeMe 24 | WORDPRESS_LOGGED_IN_SALT: ChangeMe 25 | WORDPRESS_NONCE_SALT: ChangeMe 26 | 27 | nginx: 28 | build: 29 | context: . 30 | dockerfile: docker/dk.nginx.Dockerfile 31 | image: 'changeme/changeme-nginx:staging' 32 | restart: 'no' 33 | depends_on: 34 | - php 35 | - traefik 36 | networks: 37 | - default 38 | - traefik 39 | labels: 40 | - traefik.enable=true 41 | - traefik.http.routers.${APP_NAME:-sword}.rule=Host(`staging.example.com`) 42 | - traefik.http.routers.${APP_NAME:-sword}.tls=true 43 | - traefik.http.routers.${APP_NAME:-sword}.entrypoints=websecure 44 | - traefik.http.services.${APP_NAME:-sword}.loadbalancer.server.port=80 45 | 46 | mysql: 47 | image: mysql:8.0.37 48 | restart: 'no' 49 | environment: 50 | - MYSQL_USER=ChangeMe 51 | - MYSQL_PASSWORD=ChangeMe 52 | - MYSQL_ROOT_PASSWORD=root 53 | - MYSQL_DATABASE=sword_app 54 | volumes: 55 | - mysql_data:/var/lib/mysql 56 | 57 | traefik: 58 | image: traefik:2.11 59 | ports: 60 | - '443:443' 61 | volumes: 62 | - /var/run/docker.sock:/var/run/docker.sock:ro 63 | networks: 64 | - traefik 65 | command: 66 | - --providers.docker 67 | - --providers.docker.exposedbydefault=false 68 | - --providers.docker.network=${PROJECT_DIR:-sword}_traefik 69 | - --entrypoints.web.address=:80 70 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 71 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 72 | - --entrypoints.websecure.address=:443 73 | 74 | volumes: 75 | mysql_data: 76 | 77 | networks: 78 | traefik: 79 | -------------------------------------------------------------------------------- /install/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.8" 2 | 3 | services: 4 | php: 5 | build: 6 | context: . 7 | dockerfile: docker/dk.php.Dockerfile 8 | args: 9 | PHP_VERSION: 8.1-dev 10 | restart: 'no' 11 | environment: 12 | WORDPRESS_SITE_URL: https://${APP_NAME:-sword}.localhost 13 | WORDPRESS_DB_NAME: sword_app 14 | WORDPRESS_DB_USER: user 15 | WORDPRESS_DB_PASSWORD: user 16 | WORDPRESS_DB_HOST: mysql 17 | WORDPRESS_AUTH_KEY: ChangeMe 18 | WORDPRESS_SECURE_AUTH_KEY: ChangeMe 19 | WORDPRESS_LOGGED_IN_KEY: ChangeMe 20 | WORDPRESS_NONCE_KEY: ChangeMe 21 | WORDPRESS_AUTH_SALT: ChangeMe 22 | WORDPRESS_SECURE_AUTH_SALT: ChangeMe 23 | WORDPRESS_LOGGED_IN_SALT: ChangeMe 24 | WORDPRESS_NONCE_SALT: ChangeMe 25 | WORDPRESS_DEBUG: 'true' 26 | WORDPRESS_DEBUG_DISPLAY: 'false' 27 | WORDPRESS_CONFIG_EXTRA: define('WP_ENVIRONMENT_TYPE', 'development'); 28 | volumes: 29 | - ./:/var/www/html 30 | - ./docker/dev/php/config.ini:/etc/php81/conf.d/99_dev.ini:ro 31 | extra_hosts: 32 | - host.docker.internal:host-gateway 33 | 34 | nginx: 35 | build: 36 | context: . 37 | dockerfile: docker/dk.nginx.Dockerfile 38 | restart: 'no' 39 | volumes: 40 | - ./public/:/var/www/html/public:ro 41 | - ./wp/:/var/www/html/wp:ro 42 | depends_on: 43 | - php 44 | - traefik 45 | networks: 46 | - default 47 | - traefik 48 | labels: 49 | - traefik.enable=true 50 | - traefik.http.routers.${APP_NAME:-sword}.rule=Host(`${APP_NAME:-sword}.localhost`) 51 | - traefik.http.routers.${APP_NAME:-sword}.tls=true 52 | - traefik.http.routers.${APP_NAME:-sword}.entrypoints=websecure 53 | - traefik.http.services.${APP_NAME:-sword}.loadbalancer.server.port=80 54 | 55 | mysql: 56 | image: mysql:8.0.37 57 | restart: 'no' 58 | environment: 59 | - MYSQL_USER=user 60 | - MYSQL_PASSWORD=user 61 | - MYSQL_ROOT_PASSWORD=root 62 | - MYSQL_DATABASE=sword_app 63 | volumes: 64 | - mysql_data:/var/lib/mysql 65 | 66 | mailer: 67 | image: maildev/maildev 68 | restart: 'no' 69 | networks: 70 | - default 71 | - traefik 72 | labels: 73 | - traefik.enable=true 74 | - traefik.http.routers.${APP_NAME:-sword}-mail.rule=Host(`${APP_NAME:-sword}-mail.localhost`) 75 | - traefik.http.routers.${APP_NAME:-sword}-mail.tls=true 76 | - traefik.http.routers.${APP_NAME:-sword}-mail.entrypoints=websecure 77 | - traefik.http.services.${APP_NAME:-sword}-mail.loadbalancer.server.port=1080 78 | - traefik.tcp.routers.${APP_NAME:-sword}-smtp.rule=HostSNI(`${APP_NAME:-sword}-smtp.localhost`) 79 | - traefik.tcp.routers.${APP_NAME:-sword}-smtp.entrypoints=smtpsecure 80 | - traefik.tcp.routers.${APP_NAME:-sword}-smtp.tls=true 81 | - traefik.tcp.routers.${APP_NAME:-sword}-smtp.service=${APP_NAME:-sword}-smtp 82 | - traefik.tcp.services.${APP_NAME:-sword}-smtp.loadbalancer.server.port=1025 83 | 84 | traefik: 85 | image: traefik:2.11 86 | ports: 87 | - '443:443' 88 | volumes: 89 | - /var/run/docker.sock:/var/run/docker.sock:ro 90 | networks: 91 | - traefik 92 | command: 93 | - --providers.docker 94 | - --providers.docker.exposedbydefault=false 95 | - --providers.docker.network=${PROJECT_DIR:-sword}_traefik 96 | - --entrypoints.web.address=:80 97 | - --entrypoints.web.http.redirections.entryPoint.to=websecure 98 | - --entrypoints.web.http.redirections.entryPoint.scheme=https 99 | - --entrypoints.websecure.address=:443 100 | - --entrypoints.smtp.address=:25 101 | - --entrypoints.smtpsecure.address=:465 102 | 103 | volumes: 104 | mysql_data: 105 | 106 | networks: 107 | traefik: 108 | -------------------------------------------------------------------------------- /install/docker/dev/php/config.ini: -------------------------------------------------------------------------------- 1 | apc.enable_cli = 1 2 | date.timezone = UTC 3 | session.auto_start = Off 4 | short_open_tag = Off 5 | memory_limit = 1G 6 | 7 | # Upload 8 | file_uploads = On 9 | upload_max_filesize = 64M 10 | post_max_size = 64M 11 | max_execution_time = 600 12 | 13 | realpath_cache_size = 4096K 14 | realpath_cache_ttl = 600 15 | 16 | [opcache] 17 | opcache.revalidate_freq = 1 18 | opcache.validate_timestamps = 1 19 | opcache.interned_strings_buffer = 16 20 | opcache.max_accelerated_files = 20000 21 | opcache.memory_consumption = 256 22 | 23 | [xdebug] 24 | xdebug.mode=debug 25 | xdebug.start_with_request=trigger 26 | xdebug.discover_client_host=no 27 | xdebug.client_host=host.docker.internal 28 | xdebug.client_port=9003 29 | xdebug.idekey=PHPSTORM 30 | -------------------------------------------------------------------------------- /install/docker/dk.nginx.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nginx:alpine 2 | 3 | COPY --chown=www-data:www-data . /var/www/html/ 4 | 5 | RUN cp -R /var/www/html/docker/nginx/* /etc/nginx/ \ 6 | && rm -rf /var/www/html/docker 7 | 8 | WORKDIR /var/www/html/public 9 | -------------------------------------------------------------------------------- /install/docker/dk.nginx.Dockerfile.dockerignore: -------------------------------------------------------------------------------- 1 | * 2 | !public 3 | !docker 4 | 5 | !wp 6 | wp/* 7 | 8 | !wp/core 9 | wp/core/* 10 | !wp/core/wp-admin 11 | wp/core/wp-admin/* 12 | !wp/core/wp-admin/**/*.js 13 | !wp/core/wp-admin/**/*.css 14 | !wp/core/wp-admin/**/*.png 15 | !wp/core/wp-admin/**/*.jpg 16 | !wp/core/wp-admin/**/*.jpeg 17 | !wp/core/wp-admin/**/*.gif 18 | !wp/core/wp-admin/**/*.ico 19 | !wp/core/wp-admin/**/*.svg 20 | !wp/core/wp-admin/**/*.woff 21 | !wp/core/wp-admin/**/*.woff2 22 | !wp/core/wp-admin/**/*.otf 23 | !wp/core/wp-admin/**/*.ttf 24 | !wp/core/wp-admin/**/*.xsl 25 | 26 | !wp/core/wp-includes 27 | wp/core/wp-includes/* 28 | !wp/core/wp-includes/**/*.js 29 | !wp/core/wp-includes/**/*.css 30 | !wp/core/wp-includes/**/*.png 31 | !wp/core/wp-includes/**/*.jpg 32 | !wp/core/wp-includes/**/*.jpeg 33 | !wp/core/wp-includes/**/*.gif 34 | !wp/core/wp-includes/**/*.ico 35 | !wp/core/wp-includes/**/*.svg 36 | !wp/core/wp-includes/**/*.woff 37 | !wp/core/wp-includes/**/*.woff2 38 | !wp/core/wp-includes/**/*.otf 39 | !wp/core/wp-includes/**/*.ttf 40 | !wp/core/wp-includes/**/*.xsl 41 | 42 | !wp/content 43 | wp/content/* 44 | 45 | !wp/content/plugins 46 | wp/content/plugins/* 47 | !wp/content/plugins/**/*.js 48 | !wp/content/plugins/**/*.css 49 | !wp/content/plugins/**/*.png 50 | !wp/content/plugins/**/*.jpg 51 | !wp/content/plugins/**/*.jpeg 52 | !wp/content/plugins/**/*.gif 53 | !wp/content/plugins/**/*.ico 54 | !wp/content/plugins/**/*.svg 55 | !wp/content/plugins/**/*.woff 56 | !wp/content/plugins/**/*.woff2 57 | !wp/content/plugins/**/*.otf 58 | !wp/content/plugins/**/*.ttf 59 | !wp/content/plugins/**/*.xsl 60 | 61 | !wp/content/themes 62 | wp/content/themes/* 63 | !wp/content/themes/**/*.js 64 | !wp/content/themes/**/*.css 65 | !wp/content/themes/**/*.png 66 | !wp/content/themes/**/*.jpg 67 | !wp/content/themes/**/*.jpeg 68 | !wp/content/themes/**/*.gif 69 | !wp/content/themes/**/*.ico 70 | !wp/content/themes/**/*.svg 71 | !wp/content/themes/**/*.woff 72 | !wp/content/themes/**/*.woff2 73 | !wp/content/themes/**/*.otf 74 | !wp/content/themes/**/*.ttf 75 | !wp/content/themes/**/*.xsl 76 | -------------------------------------------------------------------------------- /install/docker/dk.php.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG PHP_VERSION=8.1 2 | 3 | FROM ghcr.io/phpsword/php:$PHP_VERSION 4 | 5 | COPY --chown=www-data:www-data . /var/www/html/ 6 | 7 | RUN rm -rf /var/www/html/docker 8 | -------------------------------------------------------------------------------- /install/docker/dk.php.Dockerfile.dockerignore: -------------------------------------------------------------------------------- 1 | .git* 2 | .github 3 | .idea 4 | node_modules 5 | tests 6 | var/cache 7 | var/logs 8 | var/sessions 9 | assets 10 | public/uploads 11 | bin/phpunit 12 | 13 | */*/.htaccess 14 | *.lock 15 | package*.json 16 | *.dist 17 | *.md 18 | *.txt 19 | *.local 20 | *.ci 21 | docker-compose.* 22 | Dockerfile 23 | *.Dockerfile 24 | .dockerignore 25 | *.dockerignore 26 | Makefile 27 | .nvmrc 28 | .phpunit.* 29 | *.test 30 | *.neon 31 | *.dev 32 | webpack.config.js 33 | 34 | wp/content/uploads 35 | -------------------------------------------------------------------------------- /install/docker/nginx/conf.d/default.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | # access_log /var/log/nginx/wp-access.log; 6 | error_log /var/log/nginx/wp-error.log; 7 | 8 | # Remove X-Powered-By, which is an information leak 9 | fastcgi_hide_header X-Powered-By; 10 | 11 | root /var/www/html/public/; 12 | 13 | index index.php; 14 | 15 | location / { 16 | try_files $uri /index.php$is_args$args; 17 | } 18 | 19 | location ~ ^/index\.php(/|$) { 20 | fastcgi_pass php:9000; 21 | fastcgi_split_path_info ^(.+\.php)(/.*)$; 22 | include fastcgi_params; 23 | 24 | fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; 25 | fastcgi_param DOCUMENT_ROOT $realpath_root; 26 | fastcgi_param HTTPS on; 27 | } 28 | 29 | location ~ \.php$ { 30 | try_files $uri /index.php$is_args$args; 31 | } 32 | 33 | location = /robots.txt { 34 | allow all; 35 | log_not_found off; 36 | access_log off; 37 | } 38 | 39 | location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|eot|woff2?|otf|ttf|xsl)$ { 40 | root /; 41 | rewrite ^/wp-content/(.*)$ /content/$1 last; 42 | rewrite ^/wp-includes/(.*)$ /core/wp-includes/$1 last; 43 | rewrite ^/wp-admin/(.*)$ /core/wp-admin/$1 last; 44 | try_files /var/www/html/public$uri /var/www/html/wp$uri =404; 45 | expires max; 46 | log_not_found off; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /install/docker/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | user root; 3 | 4 | error_log /dev/stdout info; 5 | 6 | events { 7 | worker_connections 1024; 8 | } 9 | 10 | http { 11 | include /etc/nginx/mime.types; 12 | include /etc/nginx/fastcgi.conf; 13 | 14 | error_log /dev/stdout info; 15 | 16 | sendfile on; 17 | tcp_nopush on; 18 | server_names_hash_bucket_size 128; 19 | 20 | real_ip_header X-Forwarded-For; 21 | set_real_ip_from traefik; 22 | 23 | client_body_buffer_size 16K; 24 | client_body_timeout 12; 25 | client_header_buffer_size 4k; 26 | client_header_timeout 12; 27 | client_max_body_size 8M; 28 | fastcgi_buffers 16 64k; 29 | fastcgi_buffer_size 128k; 30 | keepalive_timeout 15; 31 | large_client_header_buffers 4 32k; 32 | proxy_hide_header X-Powered-By; 33 | send_timeout 10; 34 | server_tokens off; 35 | 36 | proxy_read_timeout 300; 37 | proxy_connect_timeout 300; 38 | proxy_send_timeout 300; 39 | uwsgi_read_timeout 300; 40 | fastcgi_send_timeout 300; 41 | fastcgi_read_timeout 300; 42 | 43 | gzip on; 44 | gzip_disable "msie6"; 45 | gzip_vary on; 46 | gzip_proxied any; 47 | gzip_comp_level 6; 48 | gzip_buffers 16 8k; 49 | gzip_http_version 1.1; 50 | gzip_min_length 256; 51 | gzip_types 52 | application/atom+xml 53 | application/geo+json 54 | application/javascript 55 | application/x-javascript 56 | application/json 57 | application/ld+json 58 | application/manifest+json 59 | application/rdf+xml 60 | application/rss+xml 61 | application/xhtml+xml 62 | application/xml 63 | font/eot 64 | font/otf 65 | font/ttf 66 | image/svg+xml 67 | text/css 68 | text/javascript 69 | text/plain 70 | text/xml; 71 | 72 | include /etc/nginx/conf.d/*.conf; 73 | } 74 | -------------------------------------------------------------------------------- /install/wp/wp-config.php: -------------------------------------------------------------------------------- 1 | qEz?<;dqgKIodM@Tp(aO.%?Y;[X(Si{e{6??lhHI') ); 69 | define( 'SECURE_AUTH_KEY', getenv_docker('WORDPRESS_SECURE_AUTH_KEY', 'CgT Q9P13xC+96TQ?}kt|X&?+i6lg8hxyceDQd=+<[*?_>`jE[mfOD ?eEhYB(2v') ); 70 | define( 'LOGGED_IN_KEY', getenv_docker('WORDPRESS_LOGGED_IN_KEY', '-;7}G6-h-HcyvR.Gq-X91pVW{u|_gAUE]yTR0g Ayf.[.Kj+&pFtb=DOy$;#gk-%') ); 71 | define( 'NONCE_KEY', getenv_docker('WORDPRESS_NONCE_KEY', 'd+tg52@Q#@HyUodDL8U}+%HU|lIy|O>%-i!wBmTEVFve9y#)t2k}k#D-~i+cd4:g') ); 72 | define( 'AUTH_SALT', getenv_docker('WORDPRESS_AUTH_SALT', '(zG&d7g0.qgC3ttU/*=~k|8{]%w1O68LW=j+tkJ81YtikE#GW))G[|Z9/ d!F~)kc&dp>+WoLQrV9s?D|?CgMcz') ); 74 | define( 'LOGGED_IN_SALT', getenv_docker('WORDPRESS_LOGGED_IN_SALT', 'yq7w~y03EL|ctX-|kR}v$=[E?P+[zNf8IhP@}?3-FtNY^-t|;8-8R0@K6HR!sm7o') ); 75 | define( 'NONCE_SALT', getenv_docker('WORDPRESS_NONCE_SALT', '1{1G%>#~Va8TRt-TU;pi.v{@;P-+M%g}Ud!- 4eWl](=pw?|-Rym:jGtU9|/#@4t') ); 76 | 77 | /**#@-*/ 78 | 79 | /** 80 | * WordPress Database Table prefix. 81 | * 82 | * You can have multiple installations in one database if you give each 83 | * a unique prefix. Only numbers, letters, and underscores please! 84 | */ 85 | $table_prefix = getenv_docker('WORDPRESS_TABLE_PREFIX', 'wp_'); 86 | 87 | /** 88 | * For developers: WordPress debugging mode. 89 | * 90 | * Change this to true to enable the display of notices during development. 91 | * It is strongly recommended that plugin and theme developers use WP_DEBUG 92 | * in their development environments. 93 | * 94 | * For information on other constants that can be used for debugging, 95 | * visit the Codex. 96 | * 97 | * @link https://codex.wordpress.org/Debugging_in_WordPress 98 | */ 99 | define('WP_DEBUG', filter_var(getenv_docker('WORDPRESS_DEBUG', 'false'), FILTER_VALIDATE_BOOLEAN)); 100 | 101 | /* Add any custom values between this line and the "stop editing" line. */ 102 | 103 | define('WP_DEBUG_LOG', filter_var(getenv_docker('WORDPRESS_DEBUG_LOG', 'false'), FILTER_VALIDATE_BOOLEAN)); 104 | define('WP_DEBUG_DISPLAY', filter_var(getenv_docker('WORDPRESS_DEBUG_DISPLAY', 'false'), FILTER_VALIDATE_BOOLEAN)); 105 | define('WP_DISABLE_FATAL_ERROR_HANDLER', filter_var(getenv_docker('WORDPRESS_DISABLE_FATAL_ERROR_HANDLER', 'true'), FILTER_VALIDATE_BOOLEAN)); 106 | 107 | define('WP_REDIS_HOST', getenv_docker('WORDPRESS_REDIS_HOST', 'redis')); 108 | define('WP_REDIS_PASSWORD', getenv_docker('WORDPRESS_REDIS_PASSWORD', 'password')); 109 | define('WP_CACHE_KEY_SALT', getenv_docker('WORDPRESS_CACHE_KEY_SALT', 'R!0uA_0:DmiDAX|18owsU[{9f-]+}p`,;lGaU:}}f#T}f-K%#I>:?DvpPuv|_8Bl')); 110 | 111 | define('DISABLE_WP_CRON', filter_var(getenv_docker('WORDPRESS_DISABLE_WP_CRON', 'true'), FILTER_VALIDATE_BOOLEAN)); 112 | 113 | /** WP-Optimize Cache */ 114 | define('WP_CACHE', filter_var(getenv_docker('WORDPRESS_CACHE', 'true'), FILTER_VALIDATE_BOOLEAN)); 115 | 116 | /** Automatic updates */ 117 | define('WP_HTTP_BLOCK_EXTERNAL', filter_var(getenv_docker('WORDPRESS_HTTP_BLOCK_EXTERNAL', 'false'), FILTER_VALIDATE_BOOLEAN)); 118 | define('WP_AUTO_UPDATE_CORE', filter_var(getenv_docker('WORDPRESS_AUTO_UPDATE_CORE', 'false'), FILTER_VALIDATE_BOOLEAN)); 119 | define('AUTOMATIC_UPDATER_DISABLED', filter_var(getenv_docker('WORDPRESS_AUTOMATIC_UPDATER_DISABLED', 'true'), FILTER_VALIDATE_BOOLEAN)); 120 | 121 | /** Move directories */ 122 | define('WP_CONTENT_DIR', __DIR__ . '/content'); 123 | define('WP_CONTENT_URL', getenv_docker('WORDPRESS_SITE_URL', 'https://localhost') . '/wp-content'); 124 | 125 | define('WP_PLUGIN_DIR', __DIR__ . '/content/plugins'); 126 | define('WP_PLUGIN_URL', getenv_docker('WORDPRESS_SITE_URL', 'https://localhost') . '/wp-content/plugins'); 127 | define('PLUGINDIR', __DIR__ . '/content/plugins'); 128 | 129 | 130 | /** 131 | * Handle SSL reverse proxy 132 | */ 133 | if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && str_contains($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https')) { 134 | $_SERVER['HTTPS'] = 'on'; 135 | } 136 | 137 | if ($configExtra = getenv_docker('WORDPRESS_CONFIG_EXTRA', '')) { 138 | eval($configExtra); 139 | } 140 | 141 | /* That's all, stop editing! Happy blogging. */ 142 | 143 | /** Absolute path to the WordPress directory. */ 144 | if (!defined('ABSPATH')) { 145 | define('ABSPATH', __DIR__ . '/core/'); 146 | } 147 | 148 | /** Sets up WordPress vars and included files. */ 149 | require_once ABSPATH . 'wp-settings.php'; 150 | 151 | /** Handles additional stuff in global scope. */ 152 | require_once __DIR__ . '/../vendor/phpsword/sword-bundle/src/Loader/wp-load.php'; 153 | -------------------------------------------------------------------------------- /src/Attribute/AsWordpressCommand.php: -------------------------------------------------------------------------------- 1 | getAttributes(AsWordpressCommand::class)) { 35 | return $attribute[0]->newInstance()->name; 36 | } 37 | 38 | return null; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Command/WpCommand.php: -------------------------------------------------------------------------------- 1 | ignoreValidationErrors(); 42 | } 43 | 44 | protected function execute(InputInterface $input, OutputInterface $output): int 45 | { 46 | global $wordpressLoader, $argv, $argc, $wpCliBootstrapState; 47 | 48 | $wordpressLoader = $this->wordpressLoader; 49 | 50 | foreach (WordpressGlobals::GLOBALS as $global) { 51 | global $$global; 52 | } 53 | 54 | $argv = \array_slice($argv, 1, $argc - 1); 55 | $argv[] = sprintf('--path=%s', $this->wordpressDirectory); 56 | $argv = array_unique($argv); 57 | $argc = \count($argv); 58 | 59 | foreach ($this->getBootstrapStates() as $step) { 60 | /** @var BootstrapStep $stepInstance */ 61 | $stepInstance = new $step(); 62 | $wpCliBootstrapState = $stepInstance->process($wpCliBootstrapState); 63 | } 64 | 65 | return Command::SUCCESS; 66 | } 67 | 68 | private function getBootstrapStates(): array 69 | { 70 | return [ 71 | DeclareMainClass::class, 72 | DeclareAbstractBaseCommand::class, 73 | IncludeFrameworkAutoloader::class, 74 | ConfigureRunner::class, 75 | InitializeColorization::class, 76 | InitializeLogger::class, 77 | DefineProtectedCommands::class, 78 | LoadExecCommand::class, 79 | LoadRequiredCommand::class, 80 | IncludePackageAutoloader::class, 81 | IncludeFallbackAutoloader::class, 82 | RegisterFrameworkCommands::class, 83 | RegisterDeferredCommands::class, 84 | InitializeContexts::class, 85 | LaunchRunner::class, 86 | ]; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Controller/ReauthenticateController.php: -------------------------------------------------------------------------------- 1 | headers->get('referer') === $request->getRequestUri() 25 | ? $this->redirectToRoute(Routes::WORDPRESS, [ 26 | 'path' => '' 27 | ]) 28 | : $this->redirect($request->headers->get('referer') ?: $this->generateUrl(Routes::WORDPRESS, [ 29 | 'path' => '' 30 | ])); 31 | 32 | $wordpressLoader->loadWordpress(); 33 | $userReauthenticator->reauthenticate(); 34 | 35 | return $response; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Controller/Routes.php: -------------------------------------------------------------------------------- 1 | }', name: Routes::WORDPRESS, priority: -100)] 15 | public function index(WordpressLoader $wordpressLoader, string $path): Response 16 | { 17 | return $wordpressLoader->createWordpressResponse($path); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/DependencyInjection/Compiler/WordpressPass.php: -------------------------------------------------------------------------------- 1 | processWidgets($container); 16 | $this->processPublicServices($container); 17 | } 18 | 19 | private function processWidgets(ContainerBuilder $container): void 20 | { 21 | $widgetStore = $container->findDefinition(WordpressWidgetStore::class); 22 | $taggedServices = $container->findTaggedServiceIds('sword.wordpress_widget'); 23 | 24 | foreach (array_keys($taggedServices) as $widget) { 25 | $widgetStore->addMethodCall('addWidget', [$widget]); 26 | } 27 | } 28 | 29 | /** 30 | * @param ContainerBuilder $container 31 | * @return void 32 | */ 33 | private function processPublicServices(ContainerBuilder $container): void 34 | { 35 | $publicServices = $container->getParameter('sword.public_services'); 36 | 37 | foreach ($publicServices as $service) { 38 | $definition = $container->findDefinition($service); 39 | 40 | if ($container->hasAlias($service)) { 41 | $alias = $container->getAlias($service); 42 | $alias->setPublic(true); 43 | } else { 44 | $definition->setPublic(true); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/DependencyInjection/Configuration.php: -------------------------------------------------------------------------------- 1 | getRootNode() 15 | ->children() 16 | ->scalarNode('wordpress_core_dir')->defaultValue('%kernel.project_dir%/wp/core')->end() 17 | ->scalarNode('wordpress_content_dir')->defaultValue('%kernel.project_dir%/wp/content')->end() 18 | ->scalarNode('child_theme_translation_domain')->isRequired()->end() 19 | ->scalarNode('child_theme_language_path')->defaultValue('%kernel.project_dir%/translations/%sword.child_theme_translation_domain%')->end() 20 | ->scalarNode('table_prefix')->defaultValue('wp_')->end() 21 | ->scalarNode('app_namespace')->defaultValue('App\\')->end() 22 | ->scalarNode('widgets_namespace')->defaultValue('App\\Widget\\')->end() 23 | ->scalarNode('widgets_path')->defaultValue('%kernel.project_dir%/src/Widget/')->end() 24 | ->scalarNode('wordpress_host')->defaultValue('')->end() 25 | ->arrayNode('overridden_configurations') 26 | ->scalarPrototype()->end() 27 | ->end() 28 | ->arrayNode('public_services') 29 | ->scalarPrototype()->end() 30 | ->end() 31 | ->end() 32 | ; 33 | 34 | return $treeBuilder; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/DependencyInjection/SwordExtension.php: -------------------------------------------------------------------------------- 1 | getExtensionConfig('sword'); 26 | $prioritizedConfigNames = array_diff( 27 | ['security', 'doctrine'], 28 | $swordConfig[0]['overridden_configurations'] ?? [] 29 | ); 30 | $prioritizedConfigs = []; 31 | $extensions = $container->getExtensions(); 32 | 33 | foreach (Yaml::parseFile(__DIR__ . '/../../config/app.yaml') as $name => $config) { 34 | if (empty($extensions[$name])) { 35 | continue; 36 | } 37 | 38 | if (\in_array($name, $prioritizedConfigNames, true)) { 39 | if (!\array_key_exists($name, $prioritizedConfigs)) { 40 | $prioritizedConfigs[$name] = []; 41 | } 42 | 43 | $prioritizedConfigs[$name][] = $config; 44 | } else { 45 | if ($name === 'doctrine') { 46 | $config = $this->mergeDoctrineConfig($container, $config); 47 | } 48 | 49 | $this->mergeConfigIntoOne($container, $name, $config); 50 | } 51 | } 52 | 53 | foreach ($prioritizedConfigNames as $name) { 54 | if (empty($prioritizedConfigs[$name])) { 55 | continue; 56 | } 57 | 58 | foreach ($prioritizedConfigs[$name] as $config) { 59 | if ($name === 'doctrine') { 60 | $config = $this->mergeDoctrineConfig($container, $config); 61 | } 62 | 63 | $this->mergeConfigIntoOne($container, $name, $config, true); 64 | } 65 | } 66 | } 67 | 68 | protected function loadInternal(array $mergedConfig, ContainerBuilder $container): void 69 | { 70 | $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../../config')); 71 | $loader->load('services.yaml'); 72 | 73 | $container->registerForAutoconfiguration(WordpressService::class) 74 | ->addTag('sword.wordpress_service') 75 | ; 76 | 77 | $container->setParameter('sword.wordpress_core_dir', $mergedConfig['wordpress_core_dir']); 78 | $container->setParameter('sword.wordpress_content_dir', $mergedConfig['wordpress_content_dir']); 79 | $container->setParameter( 80 | 'sword.child_theme_translation_domain', 81 | $mergedConfig['child_theme_translation_domain'], 82 | ); 83 | $container->setParameter('sword.table_prefix', $mergedConfig['table_prefix']); 84 | $container->setParameter('sword.public_services', $mergedConfig['public_services']); 85 | $container->setParameter('sword.widgets_path', $mergedConfig['widgets_path']); 86 | $container->setParameter('sword.app_namespace', $mergedConfig['app_namespace']); 87 | $container->setParameter('sword.wordpress_host', $mergedConfig['wordpress_host']); 88 | 89 | if ($mergedConfig['widgets_path'] && file_exists($mergedConfig['widgets_path'])) { 90 | $definition = (new Definition()) 91 | ->setLazy(true) 92 | ->addTag('sword.wordpress_widget') 93 | ; 94 | $loader->registerClasses($definition, $mergedConfig['widgets_namespace'], $mergedConfig['widgets_path']); 95 | } 96 | 97 | $definition = new Definition(RegexSchemaAssetFilter::class, ['~^(?!(%sword.table_prefix%))~']); 98 | $definition->addTag('doctrine.dbal.schema_filter', [ 99 | 'connection' => 'default', 100 | ]); 101 | $container->setDefinition('doctrine.dbal.default_regex_schema_filter', $definition); 102 | } 103 | 104 | private function mergeDoctrineConfig(ContainerBuilder $container, array $config): array 105 | { 106 | $doctrineConfig = $container->getExtensionConfig('doctrine'); 107 | 108 | if (isset($doctrineConfig[0]['dbal']['connections'])) { 109 | if (empty($doctrineConfig[0]['dbal']['connections']['default'])) { 110 | $doctrineConfig[0]['dbal']['connections']['default'] = $config['dbal']; 111 | } 112 | 113 | $config['dbal'] = $doctrineConfig[0]['dbal']; 114 | } 115 | 116 | return $config; 117 | } 118 | 119 | private function mergeConfigIntoOne( 120 | ContainerBuilder $container, 121 | string $name, 122 | array $config = [], 123 | bool $reverse = false, 124 | ): void { 125 | $originalConfig = $container->getExtensionConfig($name); 126 | if (!\count($originalConfig)) { 127 | $originalConfig[] = []; 128 | } 129 | 130 | $originalConfig[0] = $reverse 131 | ? $this->mergeDistinct($originalConfig[0], $config) 132 | : $this->mergeDistinct($config, $originalConfig[0]); 133 | 134 | $this->setExtensionConfig($container, $name, $originalConfig); 135 | } 136 | 137 | private function setExtensionConfig(ContainerBuilder $container, string $name, array $config = []): void 138 | { 139 | $classRef = new ReflectionClass(ContainerBuilder::class); 140 | $extensionConfigsRef = $classRef->getProperty('extensionConfigs'); 141 | 142 | $newConfig = $extensionConfigsRef->getValue($container); 143 | $newConfig[$name] = $config; 144 | $extensionConfigsRef->setValue($container, $newConfig); 145 | } 146 | 147 | private function mergeDistinct(array $first, array $second): array 148 | { 149 | foreach ($second as $index => $value) { 150 | if (\is_int($index) && !\in_array($value, $first, true)) { 151 | $first[] = $value; 152 | } elseif (!\array_key_exists($index, $first)) { 153 | $first[$index] = $value; 154 | } elseif (\is_array($value)) { 155 | if (\is_array($first[$index])) { 156 | $first[$index] = $this->mergeDistinct($first[$index], $value); 157 | } else { 158 | $first[$index] = $value; 159 | } 160 | } else { 161 | $first[$index] = $value; 162 | } 163 | } 164 | 165 | return $first; 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/Entity/WordpressEntityInterface.php: -------------------------------------------------------------------------------- 1 | 'string', 28 | ]; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Event/WooCommerceRegistrationEvent.php: -------------------------------------------------------------------------------- 1 | 'checkWordpressLoggedInStatus', 33 | ]; 34 | } 35 | 36 | public function checkWordpressLoggedInStatus(RequestEvent $event): void 37 | { 38 | $controller = $event->getRequest() 39 | ->attributes->get('_controller'); 40 | 41 | if ( 42 | !(str_starts_with($controller, 'Sword\\SwordBundle\\') || str_starts_with($controller, $this->appNamespace)) 43 | || $event->getRequest() 44 | ->attributes->get('_route') === Routes::REAUTHENTICATE 45 | || $this->tokenStorage->getToken()?->getUser() 46 | ) { 47 | return; 48 | } 49 | 50 | $host = $this->wordpressHost ?: $event->getRequest() 51 | ->getSchemeAndHttpHost(); 52 | $cookieName = 'wordpress_logged_in_' . md5($host); 53 | 54 | if (\array_key_exists($cookieName, $event->getRequest()->cookies->all())) { 55 | $this->wordpressLoader->loadWordpress(); 56 | 57 | if (!is_user_logged_in()) { 58 | $response = new RedirectResponse($event->getRequest()->getRequestUri()); 59 | $response->headers->clearCookie($cookieName); 60 | $event->setResponse($response); 61 | 62 | return; 63 | } 64 | 65 | $this->userReauthenticator->reauthenticate(); 66 | 67 | if (str_starts_with($controller, 'Sword\\SwordBundle\\')) { 68 | $event->setResponse(new RedirectResponse($host . $event->getRequest()->getRequestUri())); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/EventListener/WordpressTablePrefixEventListener.php: -------------------------------------------------------------------------------- 1 | getClassMetadata(); 25 | 26 | if ($classMetadata->isInheritanceTypeSingleTable() && !$classMetadata->isRootEntity()) { 27 | return; 28 | } 29 | 30 | if ( 31 | $classMetadata->getReflectionClass() 32 | && $classMetadata->getReflectionClass() 33 | ->implementsInterface(WordpressEntityInterface::class) 34 | ) { 35 | $classMetadata->setPrimaryTable([ 36 | 'name' => $this->prefix . $classMetadata->getTableName() 37 | ]); 38 | 39 | foreach ($classMetadata->getAssociationMappings() as $fieldName => $mapping) { 40 | if ($mapping['type'] === ClassMetadataInfo::MANY_TO_MANY) { 41 | $mappedTableName = $classMetadata->associationMappings[$fieldName]['joinTable']['name']; 42 | $classMetadata->associationMappings[$fieldName]['joinTable']['name'] = $this->prefix . $mappedTableName; 43 | } 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/EventListener/WordpressTerminateEventListener.php: -------------------------------------------------------------------------------- 1 | ['onResponse', -2048], 26 | ]; 27 | } 28 | 29 | public function onResponse(ResponseEvent $event): void 30 | { 31 | if ($event->getRequest()->attributes->get('_route') === Routes::WORDPRESS) { 32 | $response = $event->getResponse(); 33 | 34 | $response->sendHeaders(); 35 | $response->sendContent(); 36 | 37 | $this->dispatcher->dispatch( 38 | new TerminateEvent($event->getKernel(), $event->getRequest(), $response), 39 | KernelEvents::TERMINATE, 40 | ); 41 | 42 | Response::closeOutputBuffers(0, true); 43 | 44 | // Trigger WordPress register_shutdown_function() callbacks 45 | exit; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Exception/WordpressLoginSuccessfulException.php: -------------------------------------------------------------------------------- 1 | $serviceId 13 | * @return T 14 | */ 15 | function get_symfony_service(string $serviceId): mixed 16 | { 17 | /** @var WordpressLoader $wordpressLoader */ 18 | global $wordpressLoader; 19 | 20 | return $wordpressLoader->container->get($serviceId); 21 | } 22 | 23 | function get_symfony_parameter(string $parameter): mixed 24 | { 25 | /** @var WordpressLoader $wordpressLoader */ 26 | global $wordpressLoader; 27 | 28 | return $wordpressLoader->container->getParameter($parameter); 29 | } 30 | 31 | function initialize_services(): void 32 | { 33 | /** @var WordpressLoader $wordpressLoader */ 34 | global $wordpressLoader; 35 | 36 | $services = iterator_to_array($wordpressLoader->wordpressServices->getIterator()); 37 | $wordpressLoader->lazyServiceInstantiator->requireAll(); 38 | 39 | usort( 40 | $services, 41 | static fn (WordpressService $a, WordpressService $b) => $b->getPriority() <=> $a->getPriority(), 42 | ); 43 | 44 | foreach ($services as $service) { 45 | $service->initialize(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Loader/LazyServiceInstantiator.php: -------------------------------------------------------------------------------- 1 | widgetsStore->getWidgets() as $widget) { 21 | $this->load($widget); 22 | add_action('widgets_init', static fn () => register_widget($widget)); 23 | } 24 | } 25 | 26 | private function load(string $class): void 27 | { 28 | $prefix = 'App\\'; 29 | $baseDir = $this->projectDirectory . '/src/'; 30 | 31 | $length = \strlen($prefix); 32 | 33 | if (strncmp($prefix, $class, $length) !== 0) { 34 | return; 35 | } 36 | 37 | $relativeClass = substr($class, $length); 38 | $file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php'; 39 | 40 | if (file_exists($file)) { 41 | require $file; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Loader/WordpressGlobals.php: -------------------------------------------------------------------------------- 1 | 'onWooCommerceRegistration', 44 | ]; 45 | } 46 | 47 | public function onWooCommerceRegistration(WooCommerceRegistrationEvent $event): void 48 | { 49 | $this->auth = [ 50 | 'id' => $event->customerId, 51 | 'data' => $event->customerData, 52 | ]; 53 | } 54 | 55 | public function createWordpressResponse(string $urlPathName): Response 56 | { 57 | $request = $this->requestStack->getCurrentRequest(); 58 | 59 | ob_start(); 60 | $obLevel = ob_get_level(); 61 | 62 | global $wordpressLoader; 63 | $wordpressLoader = $this; 64 | 65 | foreach (WordpressGlobals::GLOBALS as $global) { 66 | global $$global; 67 | } 68 | 69 | $entryPoint = $this->wordpressDirectory . '/index.php'; 70 | 71 | if (\in_array(basename($urlPathName), ['wp-login.php', 'wp-signup.php', 'wp-comments-post.php'], true)) { 72 | $_SERVER['PHP_SELF'] = '/' . basename($urlPathName); 73 | $entryPoint = $this->wordpressDirectory . '/' . basename($urlPathName); 74 | } elseif (str_starts_with($urlPathName, 'wp-admin/')) { 75 | if ($urlPathName === 'wp-admin/') { 76 | $urlPathName = 'wp-admin/index.php'; 77 | } 78 | 79 | $_SERVER['PHP_SELF'] = '/' . $urlPathName; 80 | $entryPoint = $this->wordpressDirectory . '/' . $urlPathName; 81 | } else { 82 | $_SERVER['PHP_SELF'] = '/' . $urlPathName; 83 | } 84 | 85 | $_SERVER['SCRIPT_FILENAME'] = $entryPoint; 86 | 87 | try { 88 | require_once $entryPoint; 89 | } catch (WordpressLoginSuccessfulException $exception) { 90 | return $this->getAuthResponse($exception->username, $exception->password, $exception->rememberMe); 91 | } catch (WordpressLougoutSuccessfulException) { 92 | return new RedirectResponse($this->userAuthenticator->getLoginUrl($request)); 93 | } 94 | 95 | if ($this->auth) { 96 | wc_set_customer_auth_cookie($this->auth['id']); 97 | 98 | return $this->getAuthResponse( 99 | $this->auth['data']['user_login'] ?? '', 100 | $this->auth['data']['user_pass'] ?? '', 101 | false, 102 | ); 103 | } 104 | 105 | while (ob_get_level() > $obLevel) { 106 | ob_end_flush(); 107 | } 108 | 109 | return new Response(ob_get_clean(), is_404() ? 404 : 200); 110 | } 111 | 112 | public function loadWordpress(): void 113 | { 114 | global $wordpressLoader; 115 | $wordpressLoader = $this; 116 | 117 | foreach (WordpressGlobals::GLOBALS as $global) { 118 | global $$global; 119 | } 120 | 121 | require_once $this->wordpressDirectory . '/wp-load.php'; 122 | } 123 | 124 | private function getAuthResponse(string $username, string $password, bool $rememberMe): RedirectResponse 125 | { 126 | $session = $this->requestStack->getSession(); 127 | $session->getFlashBag() 128 | ->set( 129 | 'wp_login', 130 | [$username, $password, $rememberMe, $this->csrfTokenManager->getToken('authenticate') ->getValue()], 131 | ); 132 | 133 | return new RedirectResponse($this->requestStack->getCurrentRequest()?->getRequestUri(), 302); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/Loader/wp-load.php: -------------------------------------------------------------------------------- 1 | true 21 | ])] 22 | protected ?int $id = null; 23 | 24 | #[ORM\Column(name: 'user_login', type: 'string', length: 60)] 25 | protected ?string $login = null; 26 | 27 | #[ORM\Column(name: 'user_pass', type: 'string', length: 255)] 28 | protected ?string $pass = null; 29 | 30 | #[ORM\Column(name: 'user_nicename', type: 'string', length: 50)] 31 | protected ?string $nicename = null; 32 | 33 | #[ORM\Column(name: 'user_email', type: 'string', length: 100)] 34 | protected ?string $email = null; 35 | 36 | #[ORM\Column(name: 'user_url', type: 'string', length: 100)] 37 | protected ?string $url = null; 38 | 39 | #[ORM\Column(name: 'user_registered', type: 'datetime')] 40 | protected ?DateTime $registered; 41 | 42 | #[ORM\Column(name: 'user_activation_key', type: 'string', length: 255)] 43 | protected ?string $activationKey = null; 44 | 45 | #[ORM\Column(name: 'user_status', type: 'integer')] 46 | protected ?int $status = null; 47 | 48 | #[ORM\Column(name: 'display_name', type: 'string', length: 250)] 49 | protected ?string $displayName = null; 50 | 51 | protected ArrayCollection $metas; 52 | 53 | private array $roles = []; 54 | 55 | private array $capabilities = []; 56 | 57 | public function __construct() 58 | { 59 | $this->registered = new DateTime('1970-01-01 00:00:00'); 60 | $this->metas = new ArrayCollection(); 61 | } 62 | 63 | public function getId(): ?int 64 | { 65 | return $this->id; 66 | } 67 | 68 | public function getLogin(): ?string 69 | { 70 | return $this->login; 71 | } 72 | 73 | public function setLogin(string $login): self 74 | { 75 | $this->login = $login; 76 | 77 | return $this; 78 | } 79 | 80 | public function getPass(): ?string 81 | { 82 | return $this->pass; 83 | } 84 | 85 | public function setPass(?string $pass): self 86 | { 87 | $this->pass = $pass; 88 | 89 | return $this; 90 | } 91 | 92 | public function getNicename(): ?string 93 | { 94 | return $this->nicename; 95 | } 96 | 97 | public function setNicename(?string $nicename): self 98 | { 99 | $this->nicename = $nicename; 100 | 101 | return $this; 102 | } 103 | 104 | public function getEmail(): ?string 105 | { 106 | return $this->email; 107 | } 108 | 109 | public function setEmail(?string $email): self 110 | { 111 | $this->email = $email; 112 | 113 | return $this; 114 | } 115 | 116 | public function getUrl(): ?string 117 | { 118 | return $this->url; 119 | } 120 | 121 | public function setUrl(?string $url): self 122 | { 123 | $this->url = $url; 124 | 125 | return $this; 126 | } 127 | 128 | public function getRegistered(): ?DateTime 129 | { 130 | return $this->registered; 131 | } 132 | 133 | public function setRegistered(?DateTime $registered): self 134 | { 135 | $this->registered = $registered; 136 | 137 | return $this; 138 | } 139 | 140 | public function getActivationKey(): ?string 141 | { 142 | return $this->activationKey; 143 | } 144 | 145 | public function setActivationKey(?string $activationKey): self 146 | { 147 | $this->activationKey = $activationKey; 148 | 149 | return $this; 150 | } 151 | 152 | public function getStatus(): ?int 153 | { 154 | return $this->status; 155 | } 156 | 157 | public function setStatus(?int $status): self 158 | { 159 | $this->status = $status; 160 | 161 | return $this; 162 | } 163 | 164 | public function getDisplayName(): ?string 165 | { 166 | return $this->displayName; 167 | } 168 | 169 | public function setDisplayName(?string $displayName): self 170 | { 171 | $this->displayName = $displayName; 172 | 173 | return $this; 174 | } 175 | 176 | public function getMetas(): ArrayCollection 177 | { 178 | return $this->metas; 179 | } 180 | 181 | public function setMetas(ArrayCollection $metas): self 182 | { 183 | $this->metas = $metas; 184 | 185 | return $this; 186 | } 187 | 188 | public function getUserIdentifier(): string 189 | { 190 | return (string) $this->login; 191 | } 192 | 193 | public function getRoles(): array 194 | { 195 | $roles = $this->roles; 196 | $roles[] = 'ROLE_USER'; 197 | 198 | return array_unique($roles); 199 | } 200 | 201 | public function setRoles(array $roles): self 202 | { 203 | $this->roles = $roles; 204 | 205 | return $this; 206 | } 207 | 208 | public function getCapabilities(): array 209 | { 210 | $capabilities = $this->capabilities; 211 | 212 | return array_unique($capabilities); 213 | } 214 | 215 | public function setCapabilities(array $capabilities): self 216 | { 217 | $this->capabilities = $capabilities; 218 | 219 | return $this; 220 | } 221 | 222 | public function eraseCredentials(): void 223 | { 224 | } 225 | 226 | public function getPassword(): ?string 227 | { 228 | return $this->getPass(); 229 | } 230 | 231 | public function isEqualTo(UserInterface $user): bool 232 | { 233 | if ($user instanceof self) { 234 | $isEqual = 235 | \count($this->getRoles()) === \count($user->getRoles()) 236 | && \count($this->getCapabilities()) === \count($user->getCapabilities()) 237 | ; 238 | 239 | if ($isEqual) { 240 | foreach ($this->getRoles() as $role) { 241 | $isEqual = $isEqual && \in_array($role, $user->getRoles(), true); 242 | } 243 | 244 | foreach ($this->getCapabilities() as $capability) { 245 | $isEqual = $isEqual && \in_array($capability, $user->getCapabilities(), true); 246 | } 247 | } 248 | 249 | return $isEqual; 250 | } 251 | 252 | return false; 253 | } 254 | } 255 | -------------------------------------------------------------------------------- /src/Security/UserAuthenticator.php: -------------------------------------------------------------------------------- 1 | requestStack->getCurrentRequest(); 69 | $this->wordpressUsername = $username; 70 | $this->wordpressPassword = $request?->get('password', $request?->get('pwd', '')); 71 | $this->wordpressRememberMe = (bool) $request?->get('rememberme'); 72 | 73 | $this->onWordpressLoginSuccess(); 74 | } 75 | 76 | public function onWooCommerceLoginTerminate(): void 77 | { 78 | if (!$this->wordpressUsername || !$this->wordpressPassword) { 79 | return; 80 | } 81 | 82 | $this->onWordpressLoginSuccess(); 83 | } 84 | 85 | public function onWordpressLogout(): void 86 | { 87 | if (!$this->authorizationChecker->isGranted('ROLE_USER')) { 88 | return; 89 | } 90 | 91 | if (class_exists('WC_Session_Handler')) { 92 | $woocommerceSession = apply_filters('woocommerce_session_handler', 'WC_Session_Handler'); 93 | 94 | if ($woocommerceSession instanceof WC_Session_Handler) { 95 | $woocommerceSession->destroy_session(); 96 | } 97 | } 98 | 99 | $logoutEvent = new LogoutEvent($this->requestStack->getCurrentRequest(), $this->tokenStorage->getToken()); 100 | $this->eventDispatcher->dispatch($logoutEvent); 101 | 102 | $response = $logoutEvent->getResponse(); 103 | if (!$response instanceof Response) { 104 | $response = new RedirectResponse($this->urlGenerator->generate(Routes::WORDPRESS, [ 105 | 'path' => '' 106 | ])); 107 | } 108 | 109 | $this->tokenStorage->setToken(); 110 | $this->requestStack->getSession() 111 | ->invalidate(); 112 | 113 | throw new WordpressLougoutSuccessfulException($response); 114 | } 115 | 116 | public function onWooCommerceRegister(int $customerId, array $newCustomerData, bool $passwordGenerated): void 117 | { 118 | $this->eventDispatcher->dispatch(new WooCommerceRegistrationEvent($customerId, $newCustomerData)); 119 | } 120 | 121 | public function supports(Request $request): bool 122 | { 123 | $data = $this->requestStack->getSession() 124 | ->getFlashBag() 125 | ->get('wp_login'); 126 | 127 | if (!empty($data)) { 128 | [$this->wordpressUsername, $this->wordpressPassword, $this->wordpressRememberMe, $this->csrfToken] = $data; 129 | } 130 | 131 | return $this->wordpressUsername && $this->wordpressPassword && \in_array( 132 | $request->getPathInfo(), 133 | [$this->getLoginUrl($request), '/wp-login.php'], 134 | true, 135 | ); 136 | } 137 | 138 | public function start(Request $request, AuthenticationException $authException = null): RedirectResponse 139 | { 140 | $url = '/wp-login.php?' . http_build_query([ 141 | 'redirect_to' => $request->getUri(), 142 | 'reauth' => 0, 143 | ]); 144 | 145 | return new RedirectResponse($url, 302); 146 | } 147 | 148 | public function getLoginUrl(Request $request): string 149 | { 150 | try { 151 | $myAccountId = (int) $this->entityManager->getRepository(Option::class) 152 | ->find('woocommerce_myaccount_page_id'); 153 | 154 | return sprintf( 155 | '/%s/', 156 | $this->entityManager->getRepository(Page::class) 157 | ->find($myAccountId) 158 | ->postName, 159 | ); 160 | } catch (OptionNotFoundException) { 161 | return '/wp-login.php'; 162 | } 163 | } 164 | 165 | public function authenticate(Request $request): Passport 166 | { 167 | $badges = [new CsrfTokenBadge('authenticate', $this->csrfToken)]; 168 | 169 | if ($this->wordpressRememberMe) { 170 | $badges[] = new RememberMeBadge(); 171 | } 172 | 173 | return new Passport( 174 | new UserBadge($this->wordpressUsername), 175 | new PasswordCredentials($this->wordpressPassword), 176 | $badges, 177 | ); 178 | } 179 | 180 | public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response 181 | { 182 | $redirectPath = $this->getRedirectPath($request); 183 | 184 | if ($request->getPathInfo() === '/wp-login.php') { 185 | if ($redirectPath) { 186 | return new RedirectResponse($redirectPath); 187 | } 188 | 189 | return new RedirectResponse($this->urlGenerator->generate(Routes::WORDPRESS, [ 190 | 'path' => '' 191 | ])); 192 | } 193 | 194 | return $redirectPath 195 | ? new RedirectResponse($redirectPath) 196 | : null; 197 | } 198 | 199 | private function getRedirectPath(Request $request): ?string 200 | { 201 | $referer = $request->headers->get('referer'); 202 | 203 | if ($referer && ($queryString = parse_url($referer, PHP_URL_QUERY))) { 204 | parse_str($queryString, $result); 205 | 206 | if (!empty($result['redirect_to'])) { 207 | return $result['redirect_to']; 208 | } 209 | } 210 | 211 | return null; 212 | } 213 | 214 | private function onWordpressLoginSuccess(): never 215 | { 216 | throw new WordpressLoginSuccessfulException( 217 | $this->wordpressUsername, 218 | $this->wordpressPassword, 219 | $this->wordpressRememberMe, 220 | ); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/Security/UserProvider.php: -------------------------------------------------------------------------------- 1 | doctrine->getRepository(User::class) 27 | ->findOneBy([ 28 | 'login' => $identifier 29 | ]); 30 | 31 | $userInterop = $this->interop->getRepository(InteropUser::class) 32 | ->findOneBy([ 33 | new SelectColumns(['id', 'capabilities']), 34 | 'user_login' => $identifier, 35 | ]); 36 | 37 | $wordpressRoles = $this->interop->getRepository(Option::class) 38 | ->find($this->tablePrefix . 'user_roles'); 39 | 40 | $userRoles = []; 41 | $userCapabilities = []; 42 | 43 | foreach (array_keys($userInterop->capabilities->data, true) as $capability) { 44 | if (\array_key_exists($capability, $wordpressRoles)) { 45 | $userRoles[] = $capability; 46 | $userCapabilities = [...$userCapabilities, ...array_keys( 47 | $wordpressRoles[$capability]['capabilities'], 48 | true, 49 | )]; 50 | } else { 51 | $userCapabilities[] = $capability; 52 | } 53 | } 54 | 55 | $user->setRoles($userRoles); 56 | $user->setCapabilities($userCapabilities); 57 | 58 | return $user; 59 | } 60 | 61 | public function refreshUser(UserInterface $user): UserInterface 62 | { 63 | if (!$user instanceof User) { 64 | throw new UnsupportedUserException(sprintf('Invalid user class "%s".', \get_class($user))); 65 | } 66 | 67 | return $this->loadUserByIdentifier($user->getUserIdentifier()); 68 | } 69 | 70 | public function supportsClass(string $class): bool 71 | { 72 | return $class === User::class || is_subclass_of($class, User::class); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/Security/UserReauthenticator.php: -------------------------------------------------------------------------------- 1 | userProvider->loadUserByIdentifier(wp_get_current_user()->user_login); 31 | $this->userChecker->checkPreAuth($user); 32 | $this->managersLocator->get('main') 33 | ->authenticateUser($user, $this->authenticator, $this->requestStack->getMainRequest()); 34 | 35 | return true; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Security/Voter/CapabilityVoter.php: -------------------------------------------------------------------------------- 1 | getUser() instanceof User ? $token->getUser() : null; 17 | 18 | if (!$user instanceof User) { 19 | return $result; 20 | } 21 | 22 | foreach ($attributes as $attribute) { 23 | $result = self::ACCESS_DENIED; 24 | 25 | if (\in_array($attribute, $user->getCapabilities(), true)) { 26 | return self::ACCESS_GRANTED; 27 | } 28 | } 29 | 30 | return $result; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Security/Voter/RoleVoter.php: -------------------------------------------------------------------------------- 1 | getUser() instanceof User ? $token->getUser() : null; 23 | 24 | if (!$user instanceof User) { 25 | return $result; 26 | } 27 | 28 | foreach ($attributes as $attribute) { 29 | $result = VoterInterface::ACCESS_DENIED; 30 | 31 | if (\in_array($attribute, $this->roleHierarchy->getReachableRoleNames($user->getRoles()), true)) { 32 | return VoterInterface::ACCESS_GRANTED; 33 | } 34 | } 35 | 36 | return $result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Service/AbstractWordpressService.php: -------------------------------------------------------------------------------- 1 | widgets; 14 | } 15 | 16 | public function addWidget(string $widget): void 17 | { 18 | $this->widgets[$widget] = $widget; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/SwordBundle.php: -------------------------------------------------------------------------------- 1 | addCompilerPass(new WordpressPass()); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Translation/ChildThemeTranslationInitializer.php: -------------------------------------------------------------------------------- 1 | textDomain, $this->getLanguagesPath()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Translation/LocaleSwitcher.php: -------------------------------------------------------------------------------- 1 | localeSwitcher->setLocale(get_locale()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Widget/DefineWidgetTrait.php: -------------------------------------------------------------------------------- 1 | widget_id = $id; 17 | $this->widget_name = $name; 18 | $this->widget_description = $description; 19 | $this->widget_cssclass = $cssClass; 20 | $this->settings = $fields; 21 | } 22 | } 23 | --------------------------------------------------------------------------------