reduce duplicate code in wordpress

function site_banner_section($args = null) {
    if (!$args['title']) {

You’re getting warnings from these lines

Notice: Trying to access array offset on value of type null in C:\xampp\htdocs\wp-vs-5.3.2\wp-content\themes\personal\functions.php on line 4
Notice: Undefined index: subtitle in C:\xampp\htdocs\wp-vs-5.3.2\wp-content\themes\personal\functions.php on line 7

because $args might be null and you’re still trying to treat it as an array, and because you’re trying to access properties that may or may not exist to check their existence, which may work in other languages but isn’t how you’re meant to do this in PHP. You probably want something like:

function site_banner_section( $args = null ) {
    if ( ! is_array( $args ) ) {
        $args = array();
    }
    if ( ! ( array_key_exists( 'title', $args ) && $args['title'] ) ) {
        $args['title'] = get_the_title();

to both make sure that $args is an array before we start testing properties on it and setting properties in it, and to check whether ‘title’ exists before we try and check if it’s non-empty. Or if you’re unlikely to be passed null as an argument, only the default, then you could use

function site_banner_section( $args = array() ) {
    if ( ! ( array_key_exists( 'title', $args ) && $args['title'] ) ) {

And I’m not sure what you meant by duplicated code. If you meant avoid having to repeatedly check whether properties exist then you could use wp_parse_args to fill in the defaults, e.g.

function site_banner_section( $args = array() ) {
    $defaults = array(
        'title' => get_the_title(),
        // etc.
    );
    $args = wp_parse_args( $args, $defaults );

but then you would need to call get_the_title and get_field to populate $defaults even when you don’t actually use the values.