Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .env.slic
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,12 @@ SLIC_GIT_HANDLE=stellarwp

# The path where `slic here` was executed.
# SLIC_HERE_DIR=
# The WordPress content directory inside the containers.
SLIC_WP_CONTENT_CONTAINER_DIR=/var/www/html/wp-content
# The path from which to read plugins from.
SLIC_PLUGINS_DIR=./_plugins
# The path from which to read must-use plugins from.
SLIC_MU_PLUGINS_DIR=./_wordpress/wp-content/mu-plugins
# The path from which to read themes from.
SLIC_THEMES_DIR=./_wordpress/wp-content/themes
# The path from which to read WordPress core code from.
Expand Down
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ export PATH=$PATH:path/to/slic

When we prep a release, we follow these steps:

1. Create a branch with the version number of the release, e.g. `1.0.0`
1. Create a branch named for the release, e.g. `release/1.0.0`
2. Merge changes into that branch. This will trigger the Docker images to build.
3. Ensure that the `CLI_VERSION` has been updated in `slic.php` to reflect the version number of the release.
4. Ensure that the `changelog.md` file has been updated.
5. Merge the release branch into `main`.
6. Delete the release branch.
7. Tag the release with the changelog.md contents for that release. This will trigger the final Docker images to build.
7. Publish a GitHub Release using the changelog contents for that release. This will trigger the final Docker images to build.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,13 @@ slic here
By running `slic here` at the site level, this allows you to set plugins, themes, or the site itself as the location
from which to run tests. This also has the benefit of running tests within the WP version that your site uses.

`slic here` mounts the complete site root at `/var/www/html`. WordPress core remains at `/var/www/html/wp` when the
project installs it in `wp/`, while root installations remain at `/var/www/html`. The detected `content/` or
`wp-content/` directory is mounted at the matching container path.

`slic here` also disables subdirectory builds for sites and enables them for plugin and theme directories. You can
override the detected setting with `slic build-subdir on|off`.

![slic here](docs/images/slic-here-wp.gif)

### Preparing your project
Expand Down
8 changes: 8 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

# [2.4.0] - 2026-08-06
- Added - First-class support for site-mode projects that use `content/` instead of `wp-content/`, including plugin and theme container paths.
- Changed - `slic here` now mounts the complete project root for every WordPress site, including projects that install core into `wp/`.
- Changed - `slic here` now disables subdirectory builds for sites and enables them for plugin and theme directories.
- Fixed - Site mode no longer creates an unused `wp-content/` directory when the project uses a custom content directory.
- Fixed - Test containers now disable the object-cache drop-in without modifying the project file.
- Fixed - Airplane mode now uses and creates the configured must-use plugin directory.

# [2.3.0] - 2026-06-25
- Added - PCOV support to the slic and WordPress containers to make code coverage generation faster than Xdebug-backed coverage.
- Added - The `slic pcov on|off|status` command to configure PCOV and apply it to running containers and checks if xdebug is running and asks the user if they want to disable it.
Expand Down
4 changes: 3 additions & 1 deletion containers/slic/php.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ post_max_size=128M
;Allow long request.
max_execution_time=300

;Disable project object-cache drop-ins without modifying host files.
auto_prepend_file=/slic-disable-object-cache.php

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Im curious about as to why we should have this functionality in slic's core. Codeception is firing events which we can utilize to actually implement them in speficic cases where we need them.

For example we could utilize BEFORE/AFTER SUITE events to accomplish this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, I'm can only assume this functionality was originally added to slic to make tests stop failing because of an existing object cache and they were difficult to debug?

I don't know the actual history behind the feature, but what I do know is before this, slic was physically deleting and restoring the object-cache.php would can affect the host (e.g. remove it from a repo if it's committed because of host mapping) which is not good.

I'm open to anything after this merge to make something better, but for now I need it to not physically delete the object-cache.php file from my host machine when those are mapped in the container.

;If XDebug is active, it should always start.
xdebug.start_with_request=yes
xdebug.mode=develop,debug,coverage
Expand All @@ -27,4 +30,3 @@ opcache.validate_timestamps=1
opcache.max_accelerated_files=25000
opcache.memory_consumption=128
opcache.interned_strings_buffer=16

26 changes: 26 additions & 0 deletions containers/wordpress/disable-object-cache.php

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice usage of preinitialized hooks!

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
/**
* Disable persistent object caching inside Slic test containers.
*
* WordPress automatically loads `object-cache.php` from `WP_CONTENT_DIR` before
* normal plugins. A project's drop-in can retain data between tests or require
* services and configuration that are not part of the test environment.
*
* `slic-stack.yml` bind-mounts this file read-only at
* `/slic-disable-object-cache.php` in both the WordPress and Slic/Codeception
* containers. Their PHP configuration loads it with `auto_prepend_file`, before
* WordPress begins bootstrapping. Unlike mounting over the drop-in itself, this
* does not create, replace, or remove anything in the host content directory.
*
* WordPress supports registering filters before its plugin API is loaded using
* this array structure. When `wp-includes/plugin.php` loads, it converts the
* entry into a `WP_Hook`. Later, `wp_start_object_cache()` applies the filter and
* skips the project's drop-in. WordPress then loads its built-in, non-persistent
* `WP_Object_Cache` implementation instead.
*/
$GLOBALS['wp_filter']['enable_loading_object_cache_dropin'][10][] = [
'function' => static function () {
return false;
},
Comment on lines +22 to +24

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just seemed weird not using arrow function. Do we want to be compatible with prior 7.4 ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was no specific reason, happy to update it.

'accepted_args' => 0,
];
3 changes: 3 additions & 0 deletions containers/wordpress/php.ini
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ post_max_size=128M
;Allow long request.
max_execution_time=300

;Disable project object-cache drop-ins without modifying host files.
auto_prepend_file=/slic-disable-object-cache.php

;If XDebug is active, it should always start.
xdebug.start_with_request=yes
xdebug.mode=develop,debug,coverage
Expand Down
4 changes: 2 additions & 2 deletions docs/xdebug.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ In PHPStorm settings:
5. Set the `Host` to whatever `slic xdebug status` returns. (typically `http://localhost:8888` ... yes, put the whole thing in the `Host` field)
6. Set the `Port` to `80`
7. Check the `Use path mappings` checkbox
8. Find the `wp-content/plugins` directory and set the `Absolute path on the server` to `/var/www/html/wp-content/plugins`
8. Find the plugins directory and set the `Absolute path on the server` to the path reported by `slic xdebug status`
9. If you've added the `slic` directory to your workspace, find the `slic/_wordpress` directory and set the `Absolute path on the server` to `/var/www/html`

Screenshot from PhpStorm's video:
Expand Down Expand Up @@ -143,4 +143,4 @@ If breakpoints still don't work:
slic xdebug on
```

4. **Verify the port**: Ensure port 9001 is not blocked by a firewall and that your code editor is listening on that port.
4. **Verify the port**: Ensure port 9001 is not blocked by a firewall and that your code editor is listening on that port.
15 changes: 9 additions & 6 deletions slic-stack.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ services:
volumes:
# Paths are relative to the directory that contains this file, NOT the current working directory.
- ${SLIC_WP_DIR}:/var/www/html
- ${SLIC_PLUGINS_DIR}:/var/www/html/wp-content/plugins
- ${SLIC_THEMES_DIR}:/var/www/html/wp-content/themes
- ${SLIC_PLUGINS_DIR}:${SLIC_WP_CONTENT_CONTAINER_DIR}/plugins
- ${SLIC_THEMES_DIR}:${SLIC_WP_CONTENT_CONTAINER_DIR}/themes
- ./containers/wordpress/disable-object-cache.php:/slic-disable-object-cache.php:ro
- ${COMPOSER_CACHE_DIR:-./.cache}:/composer-cache
- ./containers/wordpress/php.ini:/usr/local/etc/php/conf.d/zz-docker.ini
healthcheck: # Apache service should be running correctly.
Expand Down Expand Up @@ -170,7 +171,7 @@ services:
# XDH=$(ip route | grep docker0 | awk '{print $9}') docker compose ...
XDEBUG_CONFIG: "idekey=${XDK:-slic} remote_enable=${XDE:-1} remote_host=${XDH:-host.docker.internal} remote_port=${XDP:-9001} client_host=${XDH:-host.docker.internal} client_port=${XDP:-9001}"
# Move to the target directory before running Codeception commands.
CODECEPTION_PROJECT_DIR: ${SLIC_CURRENT_PROJECT_CONTAINER_PATH:-/var/www/html/wp-content/plugins/test}
CODECEPTION_PROJECT_DIR: ${SLIC_CURRENT_PROJECT_CONTAINER_PATH:-${SLIC_WP_CONTENT_CONTAINER_DIR}/plugins/test}
# When running the container in shell mode (using the slic `shell` command), then use this CC configuration.
CODECEPTION_SHELL_CONFIG: "-c codeception.slic.yml"
# Whether to disable the XDebug extension in the Codeception container completely or not.
Expand All @@ -194,9 +195,11 @@ services:
# Paths are relative to the directory that contains this file, NOT the current working directory.
# Share the WordPress core installation files in the `_wordpress` directory.
- ${SLIC_WP_DIR}:/var/www/html
# Share the plugins in the `/var/www/hmtl/wp-content/plugins` directory.
- ${SLIC_PLUGINS_DIR}:/var/www/html/wp-content/plugins
- ${SLIC_THEMES_DIR}:/var/www/html/wp-content/themes
# Share plugins and themes in the configured WordPress content directory.
- ${SLIC_PLUGINS_DIR}:${SLIC_WP_CONTENT_CONTAINER_DIR}/plugins
- ${SLIC_THEMES_DIR}:${SLIC_WP_CONTENT_CONTAINER_DIR}/themes
# Disable the project's persistent object-cache drop-in without modifying the host file.
- ./containers/wordpress/disable-object-cache.php:/slic-disable-object-cache.php:ro
# In some plugins we use function-mocker and set it up to cache in `/tmp/function-mocker`.
# To avoid a long re-caching on each run, let's cache in a docker volume, caching on the host
# filesystem would be a worse cure than the disease.
Expand Down
2 changes: 1 addition & 1 deletion slic.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
] );

$cli_name = 'slic';
const CLI_VERSION = '2.3.0';
const CLI_VERSION = '2.4.0';

// If the run-time option `-q`, for "quiet", is specified, then do not print the header.
if ( in_array( '-q', $argv, true ) || ( in_array( 'exec', $argv, true ) && ! in_array( 'help', $argv, true ) ) ) {
Expand Down
4 changes: 2 additions & 2 deletions src/commands/airplane-mode.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@
$plugin_dir = $mu_plugins_dir . DIRECTORY_SEPARATOR . 'airplane-mode';

if (
! is_dir( dirname( $mu_plugins_dir ) )
&& ! mkdir( $concurrentDirectory = dirname( $mu_plugins_dir ), 0755, true )
! is_dir( $mu_plugins_dir )
&& ! mkdir( $concurrentDirectory = $mu_plugins_dir, 0755, true )
&& ! is_dir( $concurrentDirectory )
) {
echo magenta( "Failed to create mu-plugins directory {$mu_plugins_dir}." );
Expand Down
29 changes: 16 additions & 13 deletions src/commands/here.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@
$sub_args = args( [ 'reset' ], $args( '...' ), 0 );
$reset = $sub_args( 'reset', false );

$wp_dir = SLIC_ROOT_DIR . '/_wordpress';
$plugins_dir = SLIC_ROOT_DIR . '/_plugins';
$themes_dir = SLIC_ROOT_DIR . '/_wordpress/wp-content/themes';
$wp_dir = SLIC_ROOT_DIR . '/_wordpress';
$plugins_dir = SLIC_ROOT_DIR . '/_plugins';
$mu_plugins_dir = SLIC_ROOT_DIR . '/_wordpress/wp-content/mu-plugins';
$themes_dir = SLIC_ROOT_DIR . '/_wordpress/wp-content/themes';

if ( empty( $reset ) ) {
$here_dir = getcwd();
Expand All @@ -50,28 +51,30 @@
}

$has_wp_config = dir_has_wp_config( $here_dir );
$env_values = [];
$env_values = [
'SLIC_BUILD_SUBDIR' => 1,
'SLIC_MU_PLUGINS_DIR' => $mu_plugins_dir,
'SLIC_WP_CONTENT_CONTAINER_DIR' => '/var/www/html/wp-content',
];

if ( $has_wp_config ) {
if ( file_exists( "{$here_dir}/wp-content" ) ) {
$wp_content_dir = "{$here_dir}/wp-content";
} elseif ( file_exists( "{$here_dir}/content" ) ) {
$wp_content_dir = "{$here_dir}/content";
} else {
echo magenta( "Cannot locate the wp-content directory. If you have a custom wp-content location, you will need to set the SLIC_WP_DIR, SLIC_PLUGINS_DIR, and SLIC_THEMES_DIR manually in slic's .env.slic.run file." );
echo magenta( "Cannot locate the WordPress content directory." );
exit( 1 );
}

$env_values['SLIC_HERE_DIR'] = $here_dir;

// Support WP skeleton.
if ( file_exists( "{$here_dir}/wp" ) ) {
$here_dir .= '/wp';
}

$env_values['SLIC_WP_DIR'] = $here_dir;
$env_values['SLIC_PLUGINS_DIR'] = "{$wp_content_dir}/plugins";
$env_values['SLIC_THEMES_DIR'] = "{$wp_content_dir}/themes";
$env_values['SLIC_BUILD_SUBDIR'] = 0;
$env_values['SLIC_MU_PLUGINS_DIR'] = "{$wp_content_dir}/mu-plugins";
$env_values['SLIC_PLUGINS_DIR'] = "{$wp_content_dir}/plugins";
$env_values['SLIC_THEMES_DIR'] = "{$wp_content_dir}/themes";
$env_values['SLIC_WP_DIR'] = $here_dir;
$env_values['SLIC_WP_CONTENT_CONTAINER_DIR'] = '/var/www/html/' . basename( $wp_content_dir );
} else {
$env_values['SLIC_HERE_DIR'] = $here_dir;
$env_values['SLIC_WP_DIR'] = $wp_dir;
Expand Down
11 changes: 0 additions & 11 deletions src/commands/run.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,6 @@
// rather than always falling back to SLIC_PLUGINS_DIR.
$root = get_project_local_path();

// Object-cache is disruptive in the context of tests; remove the object cache drop-in before running the tests.
$object_cache_dropin = slic_wp_dir( 'wp-content/object-cache.php' );
if ( file_exists( $object_cache_dropin ) ) {
echo "Removing the object cache drop-in file before tests..." . PHP_EOL;
if ( ! unlink( $object_cache_dropin ) ) {
echo magenta( "Failed to remove the {$object_cache_dropin} file." . PHP_EOL );
exit( 1 );
}
echo "Object cache drop-in file removed." . PHP_EOL;
}

/*
* Check what configuration files we've got available.
* Depending on the what we have apply them in this order: dist, local, slic.
Expand Down
11 changes: 6 additions & 5 deletions src/project.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,24 +108,25 @@ function get_project_local_path( $target = null ) {
* @return string The container target path.
*/
function get_project_container_path( $target = null ) {
$target = $target ?: slic_target();
$target = $target ?: slic_target();
$content_dir = getenv( 'SLIC_WP_CONTENT_CONTAINER_DIR' ) ?: '/var/www/html/wp-content';

if ( empty( $target ) ) {
switch ( get_project_type() ) {
case 'plugin':
return '/var/www/html/wp-content/plugins';
return $content_dir . '/plugins';
case 'theme':
return '/var/www/html/wp-content/themes';
return $content_dir . '/themes';
default:
return '/var/www/html';
}
}

switch ( get_target_content_type( $target ) ) {
case 'plugin':
return '/var/www/html/wp-content/plugins/' . $target;
return $content_dir . '/plugins/' . $target;
case 'theme':
return '/var/www/html/wp-content/themes/' . $target;
return $content_dir . '/themes/' . $target;
case 'site':
return '/var/www/html';
}
Expand Down
13 changes: 8 additions & 5 deletions src/slic.php
Original file line number Diff line number Diff line change
Expand Up @@ -358,8 +358,7 @@ function setup_slic_env( $root_dir, $reset = false ) {
exit( 1 );
}

$wp_content_dir = ensure_dir( $wp_dir . '/wp-content' );
$wp_themes_dir = $wp_content_dir . '/themes';
$wp_themes_dir = $wp_dir . '/wp-content/themes';

putenv( 'SLIC_WP_DIR=' . $wp_dir );
putenv( 'SLIC_PLUGINS_DIR=' . ensure_dir( getenv( 'SLIC_PLUGINS_DIR' ) ?: root( '_plugins' ) ) );
Expand Down Expand Up @@ -654,7 +653,7 @@ function slic_mu_plugins_dir( $path = '' ) {
*
*/
function slic_content_type_dir( $content_type = 'plugins', $path = '' ) {
$content_type_dir = getenv( 'SLIC_' . strtoupper( $content_type ) . '_DIR' );
$content_type_dir = getenv( 'SLIC_' . str_replace( '-', '_', strtoupper( $content_type ) ) . '_DIR' );
$root_dir = root();

if ( 'plugins' === $content_type ) {
Expand All @@ -668,7 +667,7 @@ function slic_content_type_dir( $content_type = 'plugins', $path = '' ) {
if ( empty( $content_type_dir ) ) {
// Use the default directory in slic repository.
$dir = $root_dir . $default_path;
} elseif ( is_dir( $content_type_dir ) ) {
} elseif ( is_dir( $content_type_dir ) || 0 === strpos( $content_type_dir, '/' ) ) {
// Use the specified directory.
$dir = $content_type_dir;
} else {
Expand Down Expand Up @@ -876,6 +875,8 @@ function slic_info() {
'SLIC_GIT_DOMAIN',
'SLIC_GIT_HANDLE',
'SLIC_HERE_DIR',
'SLIC_MU_PLUGINS_DIR',
'SLIC_WP_CONTENT_CONTAINER_DIR',
'SLIC_PLUGINS_DIR',
'SLIC_THEMES_DIR',
'SLIC_WP_DIR',
Expand Down Expand Up @@ -1122,7 +1123,9 @@ function xdebug_status() {
echo 'Host: ' . light_cyan( 'http://localhost' . ( $localhost_port === '80' ? '' : ':' . $localhost_port ) ) . PHP_EOL;
echo colorize( 'Path mapping (host => server): <light_cyan>'
. slic_plugins_dir()
. '</light_cyan> => <light_cyan>/var/www/html/wp-content/plugins</light_cyan>' ) . PHP_EOL;
. '</light_cyan> => <light_cyan>'
. ( getenv( 'SLIC_WP_CONTENT_CONTAINER_DIR' ) ?: '/var/www/html/wp-content' )
. '/plugins</light_cyan>' ) . PHP_EOL;
echo colorize( 'Path mapping (host => server): <light_cyan>'
. slic_wp_dir()
. '</light_cyan> => <light_cyan>/var/www/html</light_cyan>' );
Expand Down