# Register functions

In `PHPER`, you can use [`add_function`](phper::modules::Module::add_function) to 
register functions.

```rust,no_run
use phper::{modules::Module, php_get_module, functions::Argument, echo};

#[php_get_module]
pub fn get_module() -> Module {
    let mut module = Module::new(
        env!("CARGO_CRATE_NAME"),
        env!("CARGO_PKG_VERSION"),
        env!("CARGO_PKG_AUTHORS"),
    );

    module.add_function("say_hello", |arguments| -> phper::Result<()> {
        let name = arguments[0].expect_z_str()?.to_str()?;
        echo!("Hello, {}!\n", name);
        Ok(())
    }).argument(Argument::new("name"));

    module
}
```

This example registers a function called `say_hello` and accepts a parameter 
`name` passed by value, similarly in PHP:

```php
<?php

function say_hello($name) {
    echo "Hello, {$name}\n";
}
```

You can get the function info by `php --re <EXTENSION_NAME>`:

```txt
Extension [ <persistent> extension #56 hello version <no_version> ] {

  - Functions {
    Function [ <internal:hello> function say_hello ] {

      - Parameters [1] {
        Parameter #0 [ <required> $name ]
      }
    }
  }
}
```

It is useful to register the parameters of the function, which can limit the 
number of parameters of the function.

Especially when the parameters need to be passed by reference.

```rust,no_run
use phper::{modules::Module, php_get_module, functions::Argument};

#[php_get_module]
pub fn get_module() -> Module {
    let mut module = Module::new(
        env!("CARGO_CRATE_NAME"),
        env!("CARGO_PKG_VERSION"),
        env!("CARGO_PKG_AUTHORS"),
    );

    module.add_function("add_count", |arguments| -> phper::Result<()> {
        let count = arguments[0].expect_mut_z_ref()?;
        *count.val_mut().expect_mut_long()? += 100;
        Ok(())
    }).argument(Argument::new("count").by_ref());

    module
}
```

Here, the argument is registered as
[`Argument::by_ref`](phper::functions::Argument::by_ref).  Therefore, the type of
the `count` parameter is no longer long, but a reference.

## Argument and return type modifiers

Arguments can have type-hints, nullability and default values applied. Here we define a function that accepts
a nullable class (in this case, an interface), and a string with a default value:

```rust,no_run
use phper::{modules::Module, php_get_module, functions::Argument, echo};
use phper::types::ArgumentTypeHint;

#[php_get_module]
pub fn get_module() -> Module {
    let mut module = Module::new(
        env!("CARGO_CRATE_NAME"),
        env!("CARGO_PKG_VERSION"),
        env!("CARGO_PKG_AUTHORS"),
    );

    module.add_function("my_function", |_| -> phper::Result<()> {
        Ok(())
    })
    .argument(Argument::new("a_class").with_type_hint(ArgumentTypeHint::ClassEntry(String::from(r"\MyNamespace\MyInterface"))).allow_null())
    .argument(Argument::new("name").with_type_hint(ArgumentTypeHint::String).with_default_value("'my_default'"))
    .argument(Argument::new("optional_bool").with_type_hint(ArgumentTypeHint::Bool).optional());

    module
}
```

The output of `php --re` for this function would look like:

```txt
    Function [ <internal:integration> function my_function ] {

      - Parameters [3] {
        Parameter #0 [ <required> ?\MyNamespace\MyInterface $a_class ]
        Parameter #1 [ <optional> string $name = 'my_default' ]
        Parameter #2 [ <optional> bool $optional_bool = <default> ]
      }
    }
```
