Passing everything to a variable by concatenation can be really boring and inconvenient, if you are wring a large amount of HTML. In that case, I would suggest using "Object Buffering". So, you can write ob_start() function from where you want to start buffering and get buffered contents using $content = ob_get_clean()
Let me give an example to make things clear.
As WordPress says, you shouldn't output directly, if you are writing a shortcode. So, the code below shouldn't be written in case of a shortcode.
<div id="my-wrapper">
<?php
$name = 'John Doe';
echo 'Hello '.$name;
?>
</div>
To fix the problem appeard (contents are displayed before title), you can put everying to a variable like below:
<?php
$content ='<div id="my-wrapper">';
$name = 'John Doe';
$content .= 'Hello '.$name;
echo '</div>';
return $content;
?>
As you can imagine, it going to be extremely annoying to write a large amount of code in this way. In this case I shall use php object buffering like below:
<?php ob_start();?>
<!-- start your code here -->
<div id="my-wrapper">
<?php
$name = 'John Doe';
echo 'Hello '.$name;
?>
</div>
<!-- code finished -->
<?php
$content = ob_get_clean();
return $content;
?>
You now don't need to change your code. Just put everything in between and
This approach can be extremely useful in case of writing a large bit of code and also enhances visibility.
Note: What ob_start() does is, it starts saving everything after it (instead of letting it being outputted). On the otherhand, ob_get_clean() stops saving things and return the saved contents.