Download the PHP package mkgrow/content-control without Composer
On this page you can find all versions of the php package mkgrow/content-control. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download mkgrow/content-control
More information about mkgrow/content-control
Files in mkgrow/content-control
Package content-control
Short Description PHPWord extension for Content Controls (Structured Document Tags) conforming to ISO/IEC 29500-1:2016 §17.5.2. Enables document-level content protection and metadata tagging in Word .docx files.
License MIT
Informations about the package content-control
ContentControl
Portuguese version: readme.pt-BR.md
ContentControl is a PHP library that extends PHPOffice/PHPWord to add Word Content Controls (Structured Document Tags/SDTs) to .docx files. It enables document-level content protection and metadata tagging conforming to ISO/IEC 29500-1:2016 §17.5.2.
Table of Contents
- Installation
- Quick Start
- Features
- Documentation
- Architecture Overview
- Core Components
- Configuration
- Error Handling
- Logging and Debugging
- Testing
- Changelog and Contributing
- Security
- Credits
- License
Installation
Install via Composer:
Requirements:
- PHP >= 8.2
- ext-dom
- ext-mbstring
- ext-zip
- phpoffice/phpword ^1.4
- ramsey/uuid ^4.7
Quick Start
Creating a New Document with Content Controls
Modifying Existing Documents
Building Tables with Content Controls (v0.6.0+)
For more examples, see the samples/ directory.
Features
Core Capabilities:
- Content Control Support: Add Word Content Controls (SDTs) to any PHPWord element
- ISO/IEC 29500-1:2016 Compliance: Full adherence to OOXML standard §17.5.2
- Document Protection: Lock SDTs, content, or both to prevent unauthorized modifications
- Template Processing: Modify existing DOCX files with XPath-based SDT location
- Run-Level SDT Wrapping: Wrap individual
<w:r>text runs with Content Controls (v0.6.0+) - TableBuilder v2: Direct PHPWord Table API with
addContentControl()delegation (v0.6.0+) - SDT Unwrap Finalization: Remove SDT wrappers while preserving visible content via
removeAllControlContents()(v0.7.1+) - GROUP SDT Replacement: Replace placeholder SDTs with complex multi-element structures
- Header/Footer Support: Add Content Controls to headers and footers (v0.2.0+)
- UUID v5 Hashing: Zero-collision element identification for template injection (v0.4.2+)
PHP Compatibility:
- PHP 8.2+ with full typed properties support
- PSR-4 autoloading (
MkGrow\ContentControlnamespace) - All classes are
final(composition over inheritance) - Immutable value objects using readonly properties
Quality Standards:
- PHPStan Level 9 static analysis with strict rules
- 82%+ code coverage with 559+ tests (Pest framework)
- Zero-collision UUID v5 hashing for element identification
- Single Responsibility Principle across all components
Supported Content Control Types:
TYPE_RICH_TEXT- Full formatting support (default)TYPE_PLAIN_TEXT- Simple text without formattingTYPE_PICTURE- Image controlsTYPE_GROUP- Container for multiple elements
Lock Types:
LOCK_NONE- No protection (default)LOCK_SDT_LOCKED- SDT cannot be deleted, content editableLOCK_CONTENT_LOCKED- SDT deletable, content read-onlyLOCK_UNLOCKED- Explicitly no lock
Supported PHPWord Elements:
- Text - Simple text nodes
- TextRun - Formatted text with multiple runs
- Table - Complete table structures
- Cell - Individual table cells (requires
inlineLevel: true) - Title - Heading elements (depth 0-9)
- Image - VML inline/floating images
Documentation
Architecture Overview
ContentControl follows a composition-based architecture with no inheritance hierarchies. All 8 core classes are final, promoting extension via composition rather than inheritance.
Design Philosophy:
- Single Responsibility: Each class has one clear purpose
- Immutability: Value objects use readonly properties for predictability
- No Duplication: v3.0+ uses DOM manipulation (not string replacement) to prevent duplicate SDTs
- Depth-First Processing: Elements sorted by depth (Cell before Table) for correct nested structures
Core Patterns:
- Proxy/Facade Pattern:
ContentControlacts as proxy for PHPWord with SDT functionality - Bridge Pattern:
TableBuilderbridges PHPWord table creation with SDT template injection - Registry Pattern:
SDTRegistrymaintains element-to-config mapping with collision-free ID generation - Service Layer:
SDTInjectoroperates as stateless service for DOM manipulation
Version Evolution:
- v1.x/v2.x: String-based XML manipulation (deprecated)
- v3.0: DOM manipulation with XPath - current standard
- v0.4.0: Inline-level SDT support with
inlineLevelflag - v0.4.2: Fluent API, UUID v5 hashing, GROUP SDT replacement
- v0.5.0: TableBuilder::setStyles() method (must be called before first addRow)
- v0.5.2: Fix content hash strategy for inline-level Text/TextRun hash collisions
- v0.6.0: Run-level SDT wrapping (CT_SdtContentRun), TableBuilder v2 with direct PHPWord Table API
- v0.7.0:
ContentProcessor::replaceContent()supportsTableBuilder, fluentend()removal completed - v0.7.1:
removeAllControlContents()now unwraps SDTs preserving visible content
Core Components
1. ContentControl
Purpose: Facade/proxy for PHPWord that adds SDT registration and injection capabilities.
Location: src/ContentControl.php
Key Methods:
__construct(?PhpWord $phpWord = null)- Create new document or wrap existing PHPWord instanceaddSection()- Delegate to PHPWord for section creationaddContentControl(object $element, array $options = []): object- Register element for SDT wrappingsave(string $filename, string $format = 'Word2007'): void- Save document with injected SDTsgetPhpWord(): PhpWord- Access underlying PHPWord instancegetSDTRegistry(): SDTRegistry- Access registry for advanced use
Workflow:
- Create document via PHPWord delegation
- Register elements with
addContentControl() - Save - triggers
SDTInjectorto wrap elements in DOM - Modified XML written to DOCX ZIP
Example:
For detailed documentation, see docs/contentcontrol.md.
2. ContentProcessor
Purpose: Open and modify existing DOCX files via XPath-based SDT manipulation.
Location: src/ContentProcessor.php
Key Methods:
__construct(string $documentPath)- Open existing DOCX for modificationfindSdt(string $tag): ?array- Locate SDT by tag (returns DOM info)replaceContent(string $tag, string|AbstractElement $value): bool- Replace entire SDT contentsetValue(string $tag, string $value): bool- Replace text preserving formattingappendContent(string $tag, AbstractElement $element): bool- Add content to existing SDTreplaceGroupContent(string $tag, ContentControl $structure): bool- Replace GROUP SDT with complex structureremoveContent(string $tag): bool- Clear SDT contentremoveAllControlContents(bool $block = false): int- Unwrap all SDTs preserving content, optionally lock documentsave(string $outputPath = ''): void- Save changes (in-place if no path provided)
Workflow:
Important: ContentProcessor is single-use. After save(), the ZIP is closed and no further modifications are possible. Create a new instance for additional changes.
For detailed documentation, see docs/contentprocessor.md.
3. TableBuilder
Purpose: Bridge pattern for creating PHPWord tables with Content Controls and injecting into templates.
Location: src/Bridge/TableBuilder.php
Two Distinct Workflows:
Workflow 1: Direct Creation with v2 API (v0.6.0+) (recommended)
Workflow 2: Template Injection (build table, inject into template placeholder)
Critical Rules:
- NEVER call
injectInto()on ContentControl - only accepts ContentProcessor - ALWAYS call
setStyles()BEFORE firstaddRow()- throws exception if table exists - Cell-level SDTs REQUIRE
inlineLevel: truein configuration - Run-level SDTs use
runLevel: trueto wrap<w:r>instead of<w:p>(v0.6.0+)
v2 API (v0.6.0+):
__construct(Table|ContentControl|null $source)- Accept Table, ContentControl, or nulladdContentControl(AbstractElement $element, array $config = []): self- Register SDT wrapping for any elementinjectInto(ContentProcessor $processor, string $targetTag): void- Inject into templatesetStyles(array $style): self- Configure table styles (must be before addRow)getContentControl(): ContentControl- Access underlying ContentControl instance
Legacy Fluent API (deprecated since v0.6.0, removal in v0.8.0):
addRow(?int $height = null, array $style = []): RowBuilderRowBuilder::addCell(int $width, array $style = []): CellBuilderCellBuilder::addText(string $text, array $style = []): selfCellBuilder::withContentControl(array $config): selfCellBuilder::end(): RowBuilder/RowBuilder::end(): TableBuilder
See Migration Guide for fluent-to-direct API migration examples.
For detailed documentation, see docs/TableBuilder.md.
Configuration
SDT Configuration Options
All Content Controls support the following configuration options:
ID Constraints:
- Exactly 8 characters
- Numeric only (0-9)
- Range: 10000000 to 99999999
- Auto-generated with collision detection if omitted
Type Constants:
ContentControl::TYPE_RICH_TEXT- Full formatting (default)ContentControl::TYPE_PLAIN_TEXT- Simple textContentControl::TYPE_PICTURE- Image controlContentControl::TYPE_GROUP- Container for multiple elements
Lock Type Constants:
ContentControl::LOCK_NONE- No protection (default)ContentControl::LOCK_SDT_LOCKED- SDT cannot be deleted, content editableContentControl::LOCK_CONTENT_LOCKED- SDT deletable, content lockedContentControl::LOCK_UNLOCKED- Explicitly no lock
Inline Level Flag:
- MUST be
truefor elements inside table cells - Default:
false(block-level elements) - Affects XPath search priority (cells before rootElement)
Run Level Flag (v0.6.0+):
- When
true, wraps individual<w:r>(text runs) instead of<w:p>(paragraphs) - Default:
false(paragraph-level wrapping) - Conforms to CT_SdtContentRun (ECMA-376 Part 4 S17.5.2.30)
- Can be combined with
inlineLevel: truefor cell-scoped run-level wrapping
Table Styles Configuration
Important: setStyles() must be called BEFORE first addRow() call. Throws ContentControlException if table already exists.
Error Handling
Exception Hierarchy
All library-specific exceptions extend MkGrow\ContentControl\Exception\ContentControlException:
Exception Types:
| Exception | Thrown When | Example Scenario |
|---|---|---|
ContentControlException |
General library errors | Invalid configuration, duplicate elements |
DocumentNotFoundException |
word/document.xml missing from DOCX |
Corrupted ZIP archive |
ZipArchiveException |
ZIP manipulation failures | File permissions, disk space |
TemporaryFileException |
Temp file cleanup fails after 3 retries | Windows file locks |
InvalidArgumentException |
Invalid parameters | Duplicate IDs, invalid SDT types |
RuntimeException |
DOM/XML processing errors | Malformed XML, serialization failures |
Best Practices:
Common Error Scenarios:
-
Duplicate Element Registration:
-
Duplicate ID:
-
Invalid ID Format:
-
Missing inlineLevel for Cell SDTs:
-
setStyles After addRow:
- injectInto on ContentControl:
Logging and Debugging
DOCX Inspection
DOCX files are ZIP archives containing XML. To inspect generated Content Controls:
Windows PowerShell:
Linux/macOS Bash:
Expected SDT Structure
Valid Content Control XML (ISO/IEC 29500-1:2016 §17.5.2):
Block-level SDT (wraps <w:p>, <w:tbl>, <w:tc>):
Run-level SDT (v0.6.0+) (wraps <w:r> inside <w:p>, CT_SdtContentRun):
Common Debugging Scenarios
-
Duplicate SDTs:
- Symptom: Multiple
<w:sdt>wrappers around same element - Cause: Using v2.x string replacement (deprecated)
- Check: Verify
SDTInjector::$processedElementsregistry marks element before wrapping - Solution: Upgrade to v3.0+ with DOM manipulation
- Symptom: Multiple
-
Missing SDTs:
- Symptom: Element not wrapped with SDT in output
- Cause:
ElementLocatorXPath query doesn't match element - Debug: Check
findElementInDOM()return value, verify element type and order - Solution: Ensure element type is supported, check registration order
-
Malformed XML:
- Symptom: Word cannot open file, reports corruption
- Cause: Invalid XML structure or namespace issues
- Debug: Use
libxml_get_errors()afterDOMDocument::loadXML() - Solution: Validate XML with
xmllint --noout temp/word/document.xml
- Namespace Pollution:
- Symptom: Redundant
xmlns:wdeclarations in SDT elements - Cause: Manual XML string creation instead of DOM methods
- Solution: Use
createElementNS()with namespace URI, relies on root inheritance
- Symptom: Redundant
PHPStan Analysis
Run PHPStan Level 9 analysis to catch type errors:
Output includes line-specific errors with context. Fix issues before committing.
Element Cache Statistics
For advanced debugging, inspect element identification cache:
Testing
Running Tests
The project uses Pest for testing with 559+ tests and 82%+ code coverage.
All Tests:
Unit Tests Only:
Feature Tests Only:
Coverage Report (Enforces 80% Minimum):
HTML Coverage Report:
Test Structure
Unit Tests (tests/Unit/):
- Test individual classes in isolation
- Mock dependencies for controllable environment
- Fast execution, no file I/O
Feature Tests (tests/Feature/):
- Integration tests with real DOCX generation
- Verify PHPWord integration
- Validate XML structure in generated files
Key Test Categories:
AdvancedHeaderFooterTest.php- SDT injection in headers/footersFluentTableBuilderTest.php- Fluent API validationGroupSdtReplacementTest.php- Complex structure replacementInlineLevelSDTTest.php- Cell-level SDT wrappingNoDuplicationTest.php- v3.0 DOM manipulation verificationImageHashCollisionTest.php- UUID v5 collision resistanceNestedSDTDetectionTest.php- Multi-level SDT preservationRunLevelSDTTest.php- Run-level<w:r>SDT wrapping (v0.6.0)TableBuilderV2Test.php- Direct Table API + addContentControl (v0.6.0)DeprecationTest.php- Deprecation notice validation (v0.6.0)ContentProcessorProtectionTest.php- SDT unwrap behavior and edge cases (v0.7.1)ContentProcessorAdvancedTest.php- Public API unwrap behavior validation (v0.7.1)
Custom Pest Expectations
The test suite includes custom expectations defined in tests/Pest.php:
Static Analysis
PHPStan Level 9:
Combined CI Check (Analysis + Coverage):
Full Check (Analysis + All Tests):
Manual Testing in Word
After generating DOCX files, verify in Microsoft Word:
- Open Developer Tab: File → Options → Customize Ribbon → Enable Developer
- View Content Controls: Developer → Design Mode
- Check Properties: Click SDT → Properties button
- Test Protection: Try editing/deleting locked SDTs
- Verify Display Names: Hover over SDT to see alias
For comprehensive Word testing checklist, see docs/MANUAL_TESTING_GUIDE.md.
Changelog and Contributing
Changelog: See CHANGELOG.md for version history and release notes.
Detailed v0.7.1 release notes: docs/0.x/CHANGELOG-v0.7.1.md
Contributing: See CONTRIBUTING.md for development guidelines, coding standards, and pull request process.
Code of Conduct: Be professional, respectful, and constructive. Focus on technical merit and project improvement.
Security
Reporting Vulnerabilities:
If you discover a security vulnerability in ContentControl, please DO NOT open a public issue or pull request.
Instead, report it privately via email to: [email protected]
Include:
- Description of the vulnerability
- Steps to reproduce
- Potential impact
- Suggested fix (if available)
We will respond within 48 hours and work with you to address the issue responsibly.
Security Best Practices:
- Never pass untrusted user input directly to
ContentProcessor::replaceContent()without validation - Validate file paths before processing to prevent directory traversal attacks
- Use
LIBXML_NONETflag (enabled by default) to prevent XXE (XML External Entity) attacks - Sanitize user-provided SDT tags and aliases
- Verify DOCX file integrity before processing
Credits
Main Contributors:
- Mateus Bandeira - Creator and Lead Developer
Third-Party Assets:
- PHPOffice/PHPWord - Core Word document manipulation
- ramsey/uuid - UUID v5 generation for collision-free hashing
- Pest - Testing framework
- PHPStan - Static analysis tool
Acknowledgments:
- ISO/IEC JTC 1/SC 34 for the Office Open XML standard
- PHPOffice community for excellent documentation and support
License
ContentControl is licensed under the MIT License. See LICENSE for details.
Documentation: docs/README.md
Samples: samples/README.md
GitHub: mateusbandeira182/ContentControl
Issues: GitHub Issues
All versions of content-control with dependencies
phpoffice/phpword Version ^1.4
ramsey/uuid Version ^4.7
ext-dom Version *
ext-mbstring Version *
ext-zip Version *