Skip to main content

Special Directives

Phlow introduces special directives that enable advanced functionality for data manipulation and scripting. Below are the available directives, explained with detailed examples.

!include

The !include directive allows you to include the content of another Phlow file. This is useful for organizing configurations or data into separate files and reusing them. Additionally, you can pass arguments to the included files for dynamic content generation.

Basic Example:

modules: !include modules.phlow

Example with Arguments:

steps:
!include ./handlers/auth.phlow target=user_login output='!phs user_data'

Example with Multiple Arguments:

steps:
!include ./templates/response.phlow status=200 message='Success' data='!phs payload'

Structure of the Included File with Arguments (auth.phlow):

- use: !arg target
input: !phs main
- log: "Processing authentication for: !arg target"
- assert: !phs !is_empty(credentials)
then:
return: !arg output
else:
return:
error: "Authentication failed"

Argument Syntax:

  • Simple values: key=value
  • Quoted values: key='value with spaces' or key="quoted value"
  • PHS expressions: key='!phs expression'

When using !arg in the included file, it will be replaced by the corresponding argument value passed in the !include directive during compile time. This means the substitution happens before the flow starts executing, creating a fully resolved Phlow structure.

Example with Multiple Includes:

config:
database: !include configs/database.phlow
server: !include configs/server.phlow

Structure of the Included File (modules.phlow):

- module1
- module2
- module3

Resulting structure:

modules:
- module1
- module2
- module3

!phs

The !phs directive allows you to execute inline scripts directly within Phlow files. It is used to capture and manipulate variables, perform calculations, execute assertions dynamically, and even call functions from modules.

Basic Example:

assert: !phs main.force
return: !phs `${main.name} is a student`

Example with Calculations:

calculated_value: !phs `${main.value} * 2`
message: !phs `The result is ${main.result}`

Example with Conditions:

return: !phs `${main.score > 50 ? 'Pass' : 'Fail'}`

Code Blocks

You can write multi-line code blocks using curly braces {}. These blocks are automatically unified into a single line during processing:

steps:
- payload: !phs {
let user = main.user;
let score = user.score || 0;
let bonus = score > 80 ? 10 : 0;

score + bonus
}

- use: log
input:
message: !phs {
let result = payload * 2;
let status = result > 100 ? "high" : "normal";

`User score: ${result} (${status})`
}

Resulting transformation:

steps:
- payload: "{{ { let user = main.user; let score = user.score || 0; let bonus = score > 80 ? 10 : 0; score + bonus } }}"

- use: log
input:
message: "{{ { let result = payload * 2; let status = result > 100 ? \"high\" : \"normal\"; `User score: ${result} (${status})` } }}"

Advanced Code Block Examples:

Data Processing:

- payload: !phs {
let users = main.users || [];
let activeUsers = users.filter(u => u.active);
let summary = {
total: users.length,
active: activeUsers.length,
inactive: users.length - activeUsers.length
};

summary
}

Complex Calculations:

- payload: !phs {
let price = main.price;
let discount = main.discount || 0;
let tax = 0.18;

let discountedPrice = price * (1 - discount);
let finalPrice = discountedPrice * (1 + tax);

#{
original: price,
discount: discount,
tax: tax,
final: Math.round(finalPrice * 100) / 100
}
}

Code Block Limitations:

  • No function declarations: You cannot declare functions inside code blocks
  • No imports: External modules must be imported using !import
  • Single expression result: The block should result in a single value

When to Use Code Blocks vs !import:

Use Code Blocks for:

  • Short, inline calculations
  • Data transformations
  • Conditional logic
  • Simple variable manipulations

Use !import for:

  • Complex function definitions
  • Reusable logic across multiple files
  • Advanced algorithms
  • Functions that need to be shared

Example Calling Module Functions:

payload: !phs `query("Select * from users where id = ${main.user_id}")`

!arg

The !arg directive is used within files that are included via !include to access arguments passed from the parent file. This directive is processed at compile time during Phlow transformation, not as a runtime variable. This means the argument values are resolved and substituted before the flow execution begins.

Basic Example:

Parent file (main.phlow):

steps:
!include ./handler.phlow target=user_authentication method=POST

Included file (handler.phlow):

- use: !arg target
input: !phs main
- log: "Processing !arg target with method !arg method"
- return:
target: !arg target
method: !arg method
processed: true

Resulting structure after compilation:

- use: user_authentication
input: !phs main
- log: "Processing user_authentication with method POST"
- return:
target: user_authentication
method: POST
processed: true

Error Handling:

If a required argument is not provided, Phlow will display a clear error message:

❌ Phlow Transformation Errors:
1. Error including file ./handler.phlow: Missing required argument: 'output'

❌ Failed to transform Phlow file: /path/to/main.phlow

Advanced Example with Conditional Logic:

Parent file:

steps:
!include ./response.phlow status=200 success=true message='Operation completed'

Included file (response.phlow):

- payload:
status_code: !arg status
success: !arg success
message: !arg message
timestamp: !phs now()

Resulting structure after compilation:

- payload:
status_code: 200
success: true
message: 'Operation completed'
timestamp: !phs now()

Important Notes:

  • Compile-time processing: All !arg references are resolved during Phlow transformation, before runtime execution
  • Static substitution: Arguments are directly substituted into the Phlow structure
  • No runtime overhead: Since processing happens at compile time, there's no performance impact during flow execution
  • Template-like behavior: This enables creating reusable Phlow templates that are customized with specific values

!import

The !import directive allows you to import a script file (.phs) for evaluation. This is useful for reusing complex logic across different parts of the project.

Basic Example:

assert: !import scripts/condition.phs

Example with Multiple Imports:

scripts:
validate: !import scripts/validate.phs
process: !import scripts/process.phs

Structure of the Imported File (scripts/condition.phs):

if (main.value > 10) {
return true;
} else {
return false;
}

Resulting structure:

assert: true

Conclusion

These directives provide powerful tools for managing and manipulating data within Phlow. By using !include with arguments, !arg, !phs, and !import, you can create modular, reusable, and maintainable workflows that enhance the overall functionality of your projects.

The argument system for !include enables you to create reusable templates and handlers that can be customized with different parameters, making your workflows more flexible and maintainable.

Additional Notes

  • Ensure that the paths provided in !include and !import are correct relative to the file where they are used.
  • When using !include with arguments, all required !arg references in the included file must have corresponding arguments provided.
  • !arg is processed at compile time: All argument substitutions happen during Phlow transformation, not during runtime execution.
  • The !phs directive can be used for both inline calculations and calling external scripts, making it versatile for various use cases.
  • When using !import, ensure that the script file is valid and contains the expected logic to avoid runtime errors.
  • The !include directive can be used to include Phlow files, while !import is specifically for .phs script files.
  • Arguments in !include support simple values, quoted strings, and PHS expressions for maximum flexibility.